useMemo 和 React.memo 都是 React 提供的性能優化工具,但它們的作用和使用場景有顯著不同。以下是兩者的全面對比:
一、核心區別總結
特性 | useMemo | React.memo |
---|---|---|
類型 | React Hook | 高階組件(HOC) |
作用對象 | 緩存計算結果 | 緩存組件渲染結果 |
優化目標 | 避免重復計算 | 避免不必要的子組件重新渲染 |
觸發條件 | 依賴項變化時重新計算 | props變化時重新渲染 |
使用位置 | 組件內部 | 組件定義外層 |
返回值 | 記憶化的值 | 記憶化的組件 |
二、useMemo 深度解析
1、基本用法
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
2、主要特點
- 緩存計算值:只有當依賴項變化時才重新計算
- 組件內部使用:只能在函數組件或自定義Hook中使用
- 不阻止渲染:只是優化計算過程,不影響組件是否渲染
3、典型場景
function Component({ items }) {// 只有items變化時才重新計算排序結果const sortedItems = useMemo(() => {return items.sort((a, b) => a.value - b.value);}, [items]);return <List items={sortedItems} />;
}
三、React.memo 深度解析
1、基本用法
const MemoizedComponent = React.memo(Component, arePropsEqual?);
注意:這里傳遞了第二個參數,它是一個自定義比較函數,用于決定是否跳過重新渲染 比如下面案例中 當判斷條件為true時候 跳過渲染
2、主要特點
- 組件記憶化:對組件進行淺比較,props不變時跳過渲染
- 類似PureComponent:用于函數組件的shouldComponentUpdate
- 可自定義比較:通過第二個參數控制比較邏輯
3、典型場景
const Child = React.memo(function Child({ data }) {return <div>{data.value}</div>;
});function Parent() {const [count, setCount] = useState(0);return (<><button onClick={() => setCount(c => c + 1)}>Re-render Parent</button><Child data={{ value: "Static" }} /> {/* 不會隨Parent重渲染 */}</>);
}
四、關鍵區別
1、作用層次不同
- useMemo:優化組件內部的計算過程
- React.memo:優化整個組件的渲染行為
2、依賴檢測方式
// useMemo 顯式聲明依賴
const value = useMemo(() => a + b, [a, b]);// React.memo 自動淺比較props
const MemoComp = React.memo(Comp);
// 或自定義比較
const MemoComp = React.memo(Comp, (prev, next) => prev.id === next.id); // 當上一次值和這次值的id 不一樣的時候 就會觸發渲染
3、性能影響對比
操作 | useMemo影響 | React.memo影響 |
---|---|---|
組件重新渲染 | 仍會執行,但可能跳過計算 | 可能完全跳過子組件渲染 |
內存占用 | 緩存計算結果 | 緩存組件實例 |
適用粒度 | 細粒度(單個值) | 粗粒度(整個組件) |
五、聯合使用示例
// 優化計算 + 優化渲染的完美組合
const ExpensiveComponent = React.memo(function({ items }) {const processedItems = useMemo(() => {return items.map(item => ({...item,fullName: `${item.firstName} ${item.lastName}`}));}, [items]);return (<ul>{processedItems.map(item => (<li key={item.id}>{item.fullName}</li>))}</ul>);
});function Parent() {const [count, setCount] = useState(0);const [items] = useState([...]);return (<><button onClick={() => setCount(c => c + 1)}>Render {count}</button><ExpensiveComponent items={items} /> {/* 父組件重渲染時,子組件不會重新渲染 */}</>);
}
六、使用建議
- 優先考慮 React.memo:當需要防止不必要的子組件重渲染時
- 合理使用 useMemo:對于計算量大的派生數據
- 不要過度優化:簡單的組件和計算不需要使用
- 注意引用類型:兩者都依賴淺比較,注意對象/數組的引用穩定性
七、常見誤區
1、錯誤期待
// 以為能阻止子組件渲染(實際無效)
const Child = () => {const data = useMemo(() => ({ value: 1 }), []);return <div>{data.value}</div>;
}
// 正確做法是用React.memo包裹組件
2、錯誤依賴
// 依賴項不全可能導致過時閉包
const value = useMemo(() => a + b + c, [a, b]); // 缺少c
3、錯誤嵌套
// 不需要用useMemo緩存React.memo組件
const MemoComp = useMemo(() => React.memo(Comp), []); // 多余
八、React.memo和React.PureComponent區別
1、PureComponent 的核心作用
- 自動實現 shouldComponentUpdate()普通 React.Component 在父組件更新或自身狀態變化時總會重新渲染,而 PureComponent 會先淺比較 props 和 state,只有數據真正變化時才會觸發渲染
- 避免不必要的渲染,適用于數據變化不頻繁或props/state 是簡單類型(非深層嵌套對象) 的場景。
2、與 React.Component 的區別
特性 | React.Component | React.PureComponent |
---|---|---|
是否自動比較 props/state | ? 每次父組件更新或自身狀態變化都會重新渲染 | ? 僅當 props/state 淺比較不同時才重新渲染 |
適用場景 | 需要手動優化或復雜數據變化時 | 數據簡單、變化不頻繁時 |
性能優化 | 需要手動實現shouldComponentUpdate() | 自動優化,減少不必要的渲染 |
3、適用場景
? 推薦使用 PureComponent 的情況:
- props/state 是基本類型(string, number, boolean 等)
- props/state 是簡單對象(沒有深層嵌套)
- 組件渲染成本高(如長列表、復雜計算)
? 不推薦使用 PureComponent 的情況:
- props/state 包含深層嵌套對象(淺比較無法檢測內部變化)
- 使用了可變數據(如直接修改數組或對象)
- 需要自定義 shouldComponentUpdate 邏輯
4、代碼示例
import React from 'react';// 使用 PureComponent 替代 Component
class MyComponent extends React.PureComponent {render() {console.log("只有 props/state 變化時才會重新渲染!");return <div>{this.props.value}</div>;}
}
5、注意事項
1、 淺比較(shallow compare)的局限性:PureComponent 只對比 props/state 的第一層,如果數據是深層嵌套的(如 { user: { name: ‘Alice’ } }),修改 user.name 不會觸發重新渲染(因為 user 對象的引用沒變
2、避免直接修改 state(應使用不可變數據)
// ? 錯誤:直接修改數組,PureComponent 無法檢測變化
this.state.items.push(newItem);
this.setState({ items: this.state.items }); // 不會觸發重新渲染// ? 正確:返回新數組
this.setState({ items: [...this.state.items, newItem] }); // 會觸發重新渲染
九、PureComponent 和 React.memo 的區別
1、適用組件類型不同
卻別 | PureComponent | React.memo |
---|---|---|
適用組件 | Class 組件 | 函數組件 |
替代方案 | 繼承 React.PureComponent | 用 React.memo() 包裹函數組件 |
示例 | class MyComp extends React.PureComponent | const MyComp = React.memo(() => {…}) |
2、比較方式
兩者都默認使用 淺比較(shallow compare),但 React.memo 更靈活:
- PureComponent:自動比較 props 和 state,只要其中任何一個變化就重新渲染
- React.memo:默認只比較 props(函數組件沒有 state),但可以自定義比較邏輯
3、對 state 的處理
PureComponent | React.memo | |
---|---|---|
是否比較 state | ? 比較 props 和 state | ? 只比較 props(函數組件依賴 useState/useReducer 管理狀態 |
狀態變化是否觸發渲染 | ? state 變化會觸發重新渲染 | ? state 變化不影響 memo(由 React Hooks 內部處理) |
4、使用場景
? PureComponent 適用場景:
- Class 組件,且 props/state 是簡單數據類型或淺層對象
- 不需要自定義 shouldComponentUpdate 邏輯
? React.memo 適用場景:
- 函數組件,且 props 是簡單數據類型或淺層對象
- 需要自定義比較邏輯(如深層比較某些 props)
5、代碼對比
(1)PureComponent(Class 組件)
import React from 'react';class MyComponent extends React.PureComponent {render() {return <div>{this.props.value}</div>;}
}
(2)React.memo(函數組件)
import React from 'react';const MyComponent = React.memo(function MyComponent(props) {return <div>{props.value}</div>;
});