React.memo:组件性能 "还能再优化一下"

在开发大型 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的作用:

  1. 减少不必要的渲染 :在父组件频繁更新时,子组件如果依赖的 props 未变化,可以避免重复渲染。

  2. 优化复杂组件:对于计算密集型组件(如表格、图表),记忆化能显著降低 CPU 开销。

  3. 提升用户体验: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 的情况

  1. 纯展示组件 :只根据 props 渲染,没有内部状态。
  2. 频繁重新渲染的组件 :父组件经常更新,但子组件依赖的 props 很少变化。
  3. 计算密集的组件:包含复杂计算或大量 DOM 操作。
  4. 列表项组件:在长列表中避免不必要的重渲染。

4.2 不适合使用 memo 的情况

  1. 简单组件 :渲染开销小,memo 的比较成本可能高于直接渲染。
  2. props 频繁变化的组件 :如果 props 经常变化,memo 无法提供显著优化。
  3. 过度优化 :过早使用 memo 可能增加代码复杂度,建议优先优化性能瓶颈。

相关推荐
curdcv_po4 分钟前
🔥🔥🔥结合 vue 或 react,去写three.js
前端·react.js·three.js
明长歌1 小时前
【javascript】Reflect学习笔记
javascript·笔记·学习
PineappleCoder1 小时前
用 “餐厅模型” 吃透事件循环:同步、宏任务、微任务谁先执行?
前端·javascript·面试
吃饭睡觉打豆豆嘛2 小时前
发布订阅模式:实现机制与工程权衡
前端·javascript
罗行2 小时前
手写Promise及相关
前端·javascript
暮星2 小时前
这次一定要讲清 ASCII & Unicode!!!
前端·javascript·html
暮星2 小时前
聊聊 JavaScript 的 ASI 机制
前端·javascript
光影少年2 小时前
js防抖、节流和扁平化实现
前端·javascript·掘金·金石计划
却尘2 小时前
React状态的人格分裂:当Vibe Coding遇上状态污染,坑你就完了。
前端·react.js·vibecoding
Neon12042 小时前
前端方案设计:实现接口缓存
前端·javascript