# 前端 React 性能检查标准
## 1. 不必要的组件重渲染 🟡
父组件状态变化导致子组件不必要地重新渲染,在昂贵组件(复杂列表、图表、编辑器)上会造成明显卡顿。
**识别信号**:子组件接收的 props 在父组件状态变化时并未改变,但子组件仍然重渲染。
```typescript
// ❌ 父组件 count 变化 → ExpensiveChild 不必要地重渲染
const Parent = ({ items }: { items: Item[] }) => {
const [count, setCount] = useState(0);
return (
<>
{items.map(item => )}
>
);
};
// ✅ 用 React.memo 跳过 props 未变化的渲染
const ExpensiveChild = React.memo(function ExpensiveChild({ data }: { data: Item }) {
return
{/* 昂贵渲染 */}
;
});
```
**注意**:`React.memo` 对 props 做浅比较,如果传入的是每次新建的对象/函数引用,memo 无效——需要配合下面的优化。
---
## 2. 渲染函数中创建对象或函数 🟡
每次渲染都创建新的对象/数组/函数引用,导致子组件的 `React.memo` 失效,或 `useEffect` 依赖项频繁触发。
```typescript
// ❌ 每次渲染都创建新的函数和对象引用
const MyComponent = ({ items }: { items: Item[] }) => {
return (
<>
{items.map(item => (
handleClick(item.id)} // 每次渲染新函数
options={{ enable: true, mode: 'edit' }} // 每次渲染新对象
/>
))}
>
);
};
// ✅ 将 id 与稳定回调分开传入 Child,避免在 map 中重新包装回调
const MyComponent = ({ items }: { items: Item[] }) => {
const handleClick = useCallback((id: string) => {
// 处理逻辑
}, []); // 依赖项为空,引用永远稳定
const options = useMemo(() => ({ enable: true, mode: 'edit' }), []);
return (
<>
{items.map(item => (
))}
>
);
};
```
**判断标准**:只有当子组件是 `React.memo` 包裹的,或该引用是某个 `useEffect`/`useCallback` 的依赖项时,才需要稳定引用。普通非 memo 组件的 props 无需此优化。
上例需要 Child 接收 `itemId` 和 `onSelect`,由 Child 内部处理点击。仅给父回调套 `useCallback`,然后仍在 `map` 中传入 `() => handleClick(item.id)`,并没有稳定传给子组件的函数引用。
---
## 3. 昂贵计算未缓存 🟡
在渲染函数中进行复杂的数组操作(sort、filter、reduce 的链式调用),每次渲染都重新计算,即使输入数据未变化。
```typescript
// ❌ 每次渲染都重新排序和过滤
const ExpensiveList = ({ items }: { items: Item[] }) => {
const sortedItems = [...items].sort((a, b) => a.value - b.value);
const filteredItems = sortedItems.filter(item => item.active);
return {filteredItems.map(item => - {item.name}
)}
;
};
// ✅ useMemo 缓存计算结果,只在 items 变化时重新计算
const ExpensiveList = ({ items }: { items: Item[] }) => {
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.value - b.value),
[items]
);
const filteredItems = useMemo(
() => sortedItems.filter(item => item.active),
[sortedItems]
);
return {filteredItems.map(item => - {item.name}
)}
;
};
```
**判断标准**:操作数组长度超过 100 条,或包含复杂排序/计算逻辑时,值得用 `useMemo` 缓存。简单的几条数据无需优化。
---
## 4. 大列表缺少虚拟化 🟢
一次性渲染几百上千个 DOM 节点,会导致首次渲染慢、滚动卡顿、内存占用高。
```typescript
// ❌ 渲染1000条数据 → 1000个真实DOM节点
const List = ({ items }: { items: Item[] }) => (
{items.map(item =>
)}
);
// ✅ 虚拟化(仅渲染可见区域的节点)
import { VariableSizeList } from 'react-window';
const VirtualList = ({ items }: { items: Item[] }) => (
50}
width="100%"
>
{({ index, style }) => (
)}
);
```
**判断标准**:列表超过 200 条且需要同时显示在页面上时,建议虚拟化。