前言: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資源