前言:Reat.memo, useMemo,useCallback是React中用于性能优化的三个核心API,它们分别针对组件渲染,计算缓存和函数引用进行优化。
一、React.memo
作用:缓存组件,当父组件重新渲染时,若子组件的props未变化,则阻止子组件重新渲染。
注意:对渲染成本极低的组件(如简单的文本展示),使用React.memo,反而增加了浅比较的开销
使用Redux的connet或自定义HOC,若未显式设置{ forwardRef: true},会导致ref或props传递中断,连带React.memo失效
const Child = React.memo(({ name }) => {console.log("子组件渲染");return <div>{name}</div>;});const Parent = () => {const [count, setCount] = useState(0);return (<div><button onClick={() => setCount(count + 1)}>点击</button><Child name="固定值" /> {/* 不会因父组件count变化而重新渲染 */}</div>);};
二、useMemo
作用:缓存计算结果,当依赖项未发生变化时避免重复计算
注意:数据规模极小,不适合使用useMemo
依赖项频繁变化,不适合用useMemo
组件本身渲染频率低,不适合用useMemo
过度嵌套useMemo,使代码难以维护,且可能引起隐蔽的依赖错误
权衡标准:数据规模>100项
计算复杂度>=O(线性对数增长)
组件渲染频率>1次/秒
缓存维护开销:浅比较成本:useMemo需比较依赖数组的每个值
内存占用:缓存结果会增加内存使用,尤其在频繁触发更新的组件中
const ExpensiveComponent = ({ list }) => {const filteredList = useMemo(() => {return list.filter(item => item.value > 10); // 复杂计算}, [list]); // 仅当list变化时重新计算return <div>{filteredList.map(item => <span key={item.id}>{item.value}</span>)}</div>;};
三、useCallback
作用:缓存函数引用,避免子组件因函数引用变化而重新渲染
注意:useCallback的依赖项过于频繁变化,导致缓存频繁失效,反而增加计算负担
过度嵌套useCallback,使代码难以维护,且可能引起隐蔽的依赖错误
const Child = React.memo(({ onClick }) => {console.log("子组件渲染");return <button onClick={onClick}>点击</button>;
});const Parent = () => {const [count, setCount] = useState(0);const handleClick = useCallback(() => {setCount(prev => prev + 1);}, []); // 依赖为空,函数引用不变return (<div><Child onClick={handleClick} /><p>Count: {count}</p></div>);
};
四、性能瓶颈的判定标准
- 可观测的渲染延迟:
组件树深层嵌套导致渲染时间超过16ms(单帧时间),引发界面卡顿
高频交互(如动画、滚动)因重复计算或渲染出现掉帧
- 不必要的子组件渲染
父组件状态变更连带触发大量子组件渲染,但子组件props实际未变化
大型列表未做虚拟滚动或key优化,导致DOM操作阻塞主线程
- 计算资源浪费
复杂计算(如数组排序,数据转换)在每次渲染时,占用CPU资源