在开发大型 React 应用时,组件频繁的重新渲染通常会导致一些性能问题,想象一下,当你点击一个按钮更新状态时,整个页面却因为不必要的子组件重渲染而卡顿。
于是这时候,
React.memo
就像一个"性能救星"登场了,它通过记忆化组件的渲染结果,避免不必要的重复渲染,从而显著提升应用性能。今天,我将通过这篇文章,带你全面了解
React.memo
的基础原理和具体实践。
一、React.memo 基础概念
1.1 React.memo
是什么?
React.memo
它是 React 提供的一个高阶组件 ,它的核心功能是记忆化组件的渲染结果 ,当组件的 props
没有发生变化时,React 便会直接复用之前的渲染结果,而不是重新执行组件函数。
举个例子:
比如你在做一个搜索框的功能,在用户搜索时,它便会根据输入的关键字,在下面显示包含该关键字的商品信息列表。
如果是传统情况下,用户每次输入一个字,React 都会重新渲染整个商品列表,包括每一个商品项,即使某个商品的信息根本没变(比如"iPhone"这个商品,只要名字没变,它对应的组件内容就不会变),但实际上它也会被重新创建和渲染。
而如果列表很长,那么这种重复渲染就会白白浪费性能,并且可能会导致页面的卡顿。
使用 React.memo
后,对于那些仍然在列表中、且信息没变 的商品(比如从"苹果"变成"苹"时,iPhone 依然匹配),React.memo
便会让 React 跳过它们的重新渲染,直接复用之前的结果。
所以说,React.memo的作用包含以下几个:
1.2 React.memo
的作用:
-
减少不必要的渲染 :在父组件频繁更新时,子组件如果依赖的
props
未变化,可以避免重复渲染。 -
优化复杂组件:对于计算密集型组件(如表格、图表),记忆化能显著降低 CPU 开销。
-
提升用户体验:React.memo可以减少重渲染次数,增加页面交互的流畅性。
二、React.memo 的基本用法
2.1 React.memo
语法示例
在函数组件中,React.memo
的使用方法是使用 React.memo
包裹住这个组件,将这个函数作为参数传递给 React.memo
:
javascript
// 函数组件形式
const MyComponent = React.memo(function MyComponent(props) {
return <div>{props.name}</div>;
});
在箭头函数中,它的使用方式更加简洁,特别是在处理简单的组件时,同样地,我们直接将箭头函数直接传递给 React.memo
,以此来达到优化的目的。
javascript
// 箭头函数形式
const MyComponent = React.memo((props) => {
return <div>{props.name}</div>;
});
2.2 React.memo
工作原理
React.memo
默认使用 浅比较 来判断 props
是否变化,简单来说,就是只比较一层,不深入去看内部结构,比如:
-
对于基本类型(字符串、数字、布尔值) ,
React.memo
是直接比较值。 -
对于引用类型(对象、数组、函数) ,
React.memo
则是通过比较引用地址,而不会深入嵌套结构。
示例1:
javascript
//这里第一次渲染时:name是 "张三",第二次渲染 name 还是"张三"
//因为两个字符串 `"张三"` 内容一样,所以 React 认为 `props` 没变。
<MyComponent name="张三" />
<MyComponent name="张三" />
//但如果按照下面将 name 改为"李四",则组件会重新渲染,因为 `props` 变了。
<MyComponent name="李四" />
示例2:
ini
// 比如这里,虽然它们的内容一样,但其实它们两个是不同的对象。
const obj1 = { age: 25 };
const obj2 = { age: 25 };
console.log(obj1 === obj2); // false!因为它们是两个不同的对象
所以,如果在 props
中传的东西是对象,那么,尽管两个对象看起来可能一样,但每次渲染都会创建一个新的对象。
ini
<MyComponent user={{ name: "张三", age: 25 }} />
<MyComponent user={{ name: "张三", age: 25 }} />
三、React.memo 的高级用法
3.1 自定义比较函数
在上面的讲解中可知,在默认情况下,React.memo
会浅比较 组件的所有 props
,而这这就带来一个问题:
即使对象的内容没变,只要每次传入的是一个新的对象 (哪怕长得一模一样),React.memo
也会认为"props变了",从而导致组件重新渲染。
于是,为了解决这个问题,React.memo
允许你传入第二个参数------一个自定义的比较函数,用来决定是否真的需要重新渲染。
javascript
const ProductList = React.memo(
({ products }) => {
console.log('ProductList 被渲染了');
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
},
(prevProps, nextProps) => {
// 判断两次的 products 中的 id 列表是否相同
const prevIds = prevProps.products.map(p => p.id);
const nextIds = nextProps.products.map(p => p.id);
// 如果 id 列表完全一样,就认为没有变化,不需要重新渲染
return prevIds.join(',') === nextIds.join(',');
}
);
在这里,我们实现了:
- 当你输入"手机"时,它会显示 iPhone
- 当你删除一个字变成"手" 后,它还是显示 iPhone
这两次的 products
虽然是不同数组,但 id
列表一样(比如都是 [1]
)。
四、React.memo适应场景?
4.1 适合使用 memo 的情况
- 纯展示组件 :只根据
props
渲染,没有内部状态。 - 频繁重新渲染的组件 :父组件经常更新,但子组件依赖的
props
很少变化。 - 计算密集的组件:包含复杂计算或大量 DOM 操作。
- 列表项组件:在长列表中避免不必要的重渲染。
4.2 不适合使用 memo 的情况
- 简单组件 :渲染开销小,
memo
的比较成本可能高于直接渲染。 - props 频繁变化的组件 :如果
props
经常变化,memo
无法提供显著优化。 - 过度优化 :过早使用
memo
可能增加代码复杂度,建议优先优化性能瓶颈。