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>;
});