做過 React 項目的應該都遇到過這種情況:開發時頁面絲般順滑,但一上線數據量大了就卡得要死。點個按鈕要等三秒,比我家貓起床都慢。
別急著甩鍋給後端,今天說個 React 裡經常被忽視但很有用的東西 —— useMemo
。
這玩意兒名字聽起來像 "有記憶",其實就是組件的省電模式。
useMemo 是什麼#
想像你有個同事,每次問他複雜問題都要算半天。關鍵是每問一次他就重算一遍,哪怕數據壓根沒變。
useMemo
就像給這個同事裝了個記憶:上次算過的結果記著呢,你要不要直接用?
本質就是緩存計算結果,只要依賴沒變就直接返回舊結果,不重複計算。
語法長這樣:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
只有當a
或b
改了才重新算,否則直接返回上次的結果。
什麼時候用#
1. 計算很重的時候#
比如處理5000
條訂單數據做排序、過濾、統計。這種操作每次都跑風扇都要起飛,而組件一天要渲染幾十次。
const processedData = useMemo(() => {
return heavyProcess(orders); // 耗時操作
}, [orders]); // 只有orders變了才重算
這樣就從每次渲染都算變成只算一次。
2. 給子組件傳對象或數組#
子組件用了React.memo
但還是一直重渲染?很可能是因為傳過去的 prop 每次都是新的引用。
比如這樣:
// 每次渲染都創建新數組,哪怕內容一樣
<MyComponent filters={[{ type: "active" }]} />
解決方法:
const stableFilters = useMemo(() => [{ type: "active" }], []);
<MyComponent filters={stableFilters} />;
這樣對象地址就不變了,React.memo
才能正常工作。
3. 作為其他 Hook 的依賴項#
const config = { userId, theme };
useEffect(() => {
fetchUserData(config);
}, [config]); // 問題來了!
每次渲染config
都是新對象,useEffect
就會重複觸發。
解決:
const config = useMemo(() => ({ userId, theme }), [userId, theme]);
useEffect(() => {
fetchUserData(config);
}, [config]);
現在只有userId
或theme
變了config
才更新。
useMemo vs useCallback#
這兩個容易搞混:
Hook | 緩存的是啥? | 使用場景 |
---|---|---|
useMemo | 一個值 | 複雜計算、對象 / 數組、作為依賴 |
useCallback | 一個函數本身 | 防止函數變化導致子組件重渲染 |
// useMemo:緩存"計算結果"
const total = useMemo(() => items.reduce(sum), [items]);
// useCallback:緩存"函數本身"
const handleClick = useCallback(() => doSomething(id), [id]);
常見坑點#
依賴項寫錯#
useMemo(() => expensive(), [{}]); // 每次都是新對象!
useMemo(() => expensive(), [[]]); // 每次都是新數組!
確保依賴項是穩定的值。
濫用 useMemo#
不是所有計算都要緩存。如果只是簡單的a + b
,用useMemo
反而浪費。
React 官方說過:過早優化是萬惡之源。先找到性能瓶頸再用useMemo
。
總結#
什麼時候用:
- 計算很重
- 傳給
memo
子組件的對象 / 數組 - 作為其他
Hook
的依賴項
記住:它是瑞士軍刀,不是創可貼。
頁面卡了別急著換框架,先看看是不是該給 useMemo
發工資了。