这是 React 面试中高频考点,很多人会背概念,但面试官更喜欢问:
PureComponent、memo、useMemo、useCallback 有什么区别?什么时候用?为什么能减少渲染?
你可以这样理解:
React 渲染原理
父组件重新渲染时:
javascript
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>
+
</button>
<Child />
</>
);
}
点击按钮后:
Parent render
Child render
即使 Child 的 props 没变:
xml
<Child />
React 默认仍会重新执行 Child 函数。
这就是性能浪费。
1. PureComponent
类组件专用
scala
class Child extends React.PureComponent {
render() {
console.log("child render");
return <div>Child</div>;
}
}
相当于:
javascript
shouldComponentUpdate(
nextProps,
nextState
){
return false;
}
但实际上内部做的是:
vbnet
浅比较(Shallow Compare)
比较:
ini
oldProps === newProps
oldState === newState
相同:
不更新
不同:
重新渲染
2. React.memo
函数组件版本 PureComponent
javascript
const Child = React.memo(() => {
console.log("child render");
return <div>Child</div>;
});
父组件:
javascript
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>
+
</button>
<Child />
</>
);
}
点击按钮:
Parent render
Child 不会重新渲染。
React.memo原理
内部也是:
scss
shallowEqual(oldProps, newProps)
浅比较。
memo失效场景
ini
<Child user={{ name: "Tom" }} />
每次 render:
diff
{}
!==
{}
引用变了。
所以:
Child render
Child render
Child render
memo失效。
3. useMemo
缓存值
很多人误以为:
useMemo 防止组件渲染
错。
它缓存的是:
计算结果
例如
ini
const total = useMemo(() => {
return bigData.reduce(...)
}, [bigData]);
第一次:
执行reduce
之后:
直接返回缓存结果
不会重复计算。
不用 useMemo
ini
const total = bigData.reduce(...)
Parent 每 render 一次:
reduce执行一次
数据大时非常耗性能。
useMemo + memo
经典组合:
ini
const user = useMemo(() => {
return {
name: "Tom"
};
}, []);
<Child user={user}/>
这样:
sql
user引用固定
memo 才能生效。
4. useCallback
缓存函数
很多人写:
ini
<Child
onClick={() => {
console.log("click");
}}
/>
每次 render:
javascript
new Function()
都会生成新函数。
即使用了 memo:
ini
const Child = memo(...)
也会重新渲染。
因为:
yaml
oldFn !== newFn
解决:
ini
const handleClick = useCallback(() => {
console.log("click");
}, []);
ini
<Child onClick={handleClick}/>
这样:
函数引用固定
memo 生效。
useMemo vs useCallback
很多面试官喜欢问。
本质:
scss
useCallback(fn,deps)
等于:
scss
useMemo(() => fn, deps)
源码思想就是这样。
缓存值:
ini
const user = useMemo(
() => ({ name: "Tom" }),
[]
);
缓存函数:
ini
const fn = useCallback(
() => {},
[]
);
面试总结
| API | 作用 | 适用 |
|---|---|---|
| PureComponent | 类组件避免重复渲染 | Class |
| React.memo | 函数组件避免重复渲染 | Function |
| useMemo | 缓存计算结果 | 值 |
| useCallback | 缓存函数引用 | 函数 |
记住一句话:
PureComponent / memo
解决「组件重复渲染」
useMemo
解决「重复计算」
useCallback
解决「函数引用变化导致的重复渲染」
实际项目中的最佳实践
在你做 React 管理后台(Ant Design、ECharts、大表格)时,最常见的优化组合是:
ini
const columns = useMemo(() => [...], []);
const handleSearch = useCallback(() => {
...
}, []);
const TableList = memo(TableListComponent);
特别是:
- Ant Design Table
- ECharts 图表
- 大型表单
- 树形组件