文章目录

React 性能优化:memo、useMemo、useCallback

发布于 2026-04-06 15:12:48 · 浏览 16 次 · 评论 0 条

React 组件的渲染机制默认是“牵一发而动全身”:父组件更新,所有子组件都会跟随重渲染。当组件树变得复杂时,这会造成巨大的性能浪费。

通过 React.memouseMemouseCallback 这三个工具,你可以精准控制组件的更新时机,切断不必要的渲染链条。


1. 使用 React.memo 阻止组件重渲染

React.memo 是一个高阶组件,用于包裹整个组件。它的作用是:对比 前后的 Props,如果 Props 没变,就跳过 该组件的渲染,直接复用上一次的结果。

适用场景:纯函数组件、渲染开销较大、Props 经常保持不变的子组件。

打开 子组件文件。

引入 React

使用 React.memo() 包裹 组件导出。

import React from 'react';

// 普通组件定义
const MyComponent = ({ name }) => {
  console.log('子组件渲染了');
  return <div>{name}</div>;
};

// 使用 memo 包裹后导出
export default React.memo(MyComponent);

验证 效果。当父组件更新但 name 属性未变化时,控制台不会打印“子组件渲染了”。


2. 理解引用相等性问题

仅仅使用 React.memo 往往不够。React 组件在每次渲染时,内部声明的对象 {} 和函数 () => {} 都会生成全新的内存地址。

如果你将一个“新生成的函数”或“新生成的对象”作为 Props 传给被 memo 包裹的子组件,React.memo 会认为 Props 发生了变化(因为内存地址变了),从而触发重渲染。

graph TD A["Parent Render"] --> B["Create New Function/Object"] B --> C["Pass Props to Child"] C --> D{"Props Reference Changed?"} D -- "Yes (New Reference)" --> E["Child Re-renders (memo fails)"] D -- "No (Same Reference)" --> F["Child Skips Render (memo works)"]

要解决这个问题,必须配合 useCallbackuseMemo 来稳定引用地址。


3. 使用 useCallback 缓存函数

useCallback 用于缓存函数实例。它确保在依赖项不变的情况下,函数在多次渲染中保持相同的内存地址。

适用场景:将函数作为 Props 传递给子组件、函数作为其他 Hook 的依赖项。

找到 父组件中传递给子组件的函数。

引入 useCallback

包裹 函数定义。

声明 依赖项数组。

import React, { useCallback } from 'react';

const ParentComponent = () => {
  // 每次渲染都返回同一个函数引用,除非 count 变化
  const handleClick = useCallback(() => {
    console.log('点击事件触发');
  }, []); // 空数组表示该函数永远不变

  return <ChildComponent onClick={handleClick} />;
};

核心语法:

const cachedFn = useCallback(fn, dependencies);

4. 使用 useMemo 缓存计算结果

useMemo 用于缓存计算结果(返回值)。它可以避免在每次渲染时重复执行昂贵的计算,也可以用于缓存对象以保持引用相等。

适用场景:复杂的数学计算、数据转换、需要保持引用稳定的对象 Props。

识别 组件中开销较大的计算逻辑或对象构造。

引入 useMemo

包裹 计算逻辑。

声明 依赖项数组。

import React, { useMemo } from 'react';

const ParentComponent = ({ items }) => {
  // 只有 items 变化时才重新计算,否则直接返回缓存结果
  const sortedList = useMemo(() => {
    console.log('正在进行昂贵的排序...');
    return items.sort((a, b) => a - b);
  }, [items]);

  // 缓存对象,防止传递给子组件时因引用变化导致重渲染
  const styleObj = useMemo(() => ({ color: 'red' }), []);

  return <ChildComponent list={sortedList} style={styleObj} />;
};

核心语法:

const cachedValue = useMemo(calculateValue, dependencies);

5. 区分三者使用场景

这三个 API 经常被混淆,以下是快速决策指南。

API 缓存内容 核心目的 典型应用场景
memo 组件 (JSX) 跳过组件渲染 子组件 Props 变化少,子组件渲染开销大
useCallback 函数 保持函数引用稳定 传递函数给子组件;函数作为 useEffect 依赖
useMemo 任意值 避免重复计算/保持对象引用 复杂数据计算;传递对象/数组给子组件

6. 实战优化流程

在实际开发中,按照以下步骤操作可避免过度优化或优化无效。

定位 性能瓶颈。使用 React DevTools 的 Profiler 面板查看 哪些组件频繁重渲染。

检查 子组件。如果子组件重渲染但 Props 未变,添加 React.memo

检查 Props 来源。如果 memo 失效,检查 父组件传递的是否为引用类型(函数、对象、数组)。

修复 引用问题。

  1. 若为函数,使用 useCallback 包裹。
  2. 若为对象或计算结果,使用 useMemo 包裹。

验证 优化结果。再次运行 Profiler,确认 无关的状态更新不再触发子组件渲染。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文