React.memo 和 useMemo 的区别定义解析(附:使用场景与性能对比)

React.memo 决定一个组件要不要重新渲染,useMemo 决定一次计算要不要重新执行,前者作用于组件边界,后者作用于渲染过程内部,两者可以配合使用但不能互相替代。在一个数据看板项目里,搜索框每敲一个字,整棵列表子树就跟着重渲染,把过滤逻辑收进 useMemo、把列表组件包上 React.memo 之后,输入卡顿才消失。把两者混为一谈,或者不加测量地到处包裹,比较开销和内存占用会悄悄上涨,这是性能优化里最常见的反直觉陷阱。

用法对比:一个是高阶组件,一个是 Hook

React.memo 是高阶组件,接收一个组件并返回包裹后的新组件;useMemo 是 Hook,只能在函数组件或自定义 Hook 的顶层调用,用来缓存一次计算的结果。这个身份差异决定了它们的调用位置、参数形式和组合方式完全不同,下面的表格把五个基础维度放在一起对照:

维度 React.memo useMemo
本质 高阶组件(HOC) React Hook
接收参数 组件 + 可选的自定义比较函数 计算函数 + 依赖数组
返回结果 包裹后的新组件 缓存的计算值
调用位置 模块顶层或组件导出处 函数组件 / 自定义 Hook 顶层
类组件中能否使用 可以包裹函数组件后导出 不能,Hook 仅限函数组件

身份不同带来的约束也很实际:useMemo 遵守 Hooks 规则,不能放进条件分支、循环或普通函数里;React.memo 没有这个限制,它通常出现在模块导出的位置,把「是否重渲染」的判断从组件内部挪到组件外部。一个管组件整体的渲染开关,一个管渲染过程中某一个值的取用,先记住这条分工线,后面的差异都能推导出来。

一段可运行的代码把两者的典型配合演示出来:父组件负责过滤数据,子组件只关心展示。

import { memo, useMemo } from 'react';

// React.memo:包裹组件,props 未变时跳过重渲染
const PriceList = memo(function PriceList({ items }) {
  return (
    <ul>
      {items.map((it) => (
        <li key={it.id}>{it.name}:{it.price} 元</li>
      ))}
    </ul>
  );
});

export default function Dashboard({ orders, keyword }) {
  // useMemo:缓存过滤结果,keyword 与 orders 未变时不重算
  const visibleOrders = useMemo(
    () => orders.filter((o) => o.title.includes(keyword)),
    [orders, keyword]
  );

  return <PriceList items={visibleOrders} />;
}

这个组合的关键在于双方配合:useMemo 让 visibleOrders 的引用在依赖不变时保持稳定,React.memo 的浅比较才有机会命中。如果过滤结果每次渲染都是新数组,包 memo 就形同虚设。

触发机制对比:浅比较 props 还是监听依赖数组

React.memo 用新旧 props 的浅比较决定是否跳过渲染,useMemo 用依赖数组的逐项比较决定是否复用上次的计算结果。两者的比较策略看似接近,比较的对象和影响范围却不一样:

维度 React.memo useMemo
比较对象 新旧两组 props 依赖数组里的每一项
比较方式 浅比较(逐项 Object.is) 逐项 Object.is
是否支持自定义 可传入第二个参数自定义比较函数 不支持,依赖项固定
命中后跳过的内容 整个子树的渲染过程 一次函数执行
组件自身 state 变化 仍会正常重渲染 依赖不变则照常复用缓存值
Context 变化 订阅的 context 更新仍触发渲染 与 context 无关

浅比较的边界值得单独强调:Object.is 只比较一层引用,两个内容相同的对象字面量、每次渲染新建的内联函数,都会被判为「变化」。所以给 memo 组件传属性时,内联对象和内联函数是头号破坏者,这也是 useCallback 存在的理由——它把函数引用固定住,让 React.memo 的浅比较能通过。另外要留意 React 官方文档的口径:memoization 是一种性能优化手段,不是语义保证,React 在特定情况下(比如为离屏组件释放内存)可能丢弃缓存值并重新计算,代码逻辑必须在没有这些优化的前提下依然正确。

性能开销对比:一个省渲染,一个省计算

两者优化的成本类型不同——React.memo 省的是子树协调与重渲染的开销,useMemo 省的是重复计算的开销,而它们自身都引入了新的成本项。认清这张收支表,才能判断包裹是否划算:

维度 React.memo useMemo
优化目标 减少子树 re-render 减少重复执行昂贵计算
自身开销 每次渲染都要做 props 浅比较 依赖数组比较 + 缓存持有
内存影响 需缓存上次渲染结果 需持有缓存值,依赖多时更明显
滥用的后果 比较成本超过渲染收益,白做功 内存占用上升,可读性下降
state 更新时 无法跳过,正常重渲染 依赖不变则不重算,收益保留

实践里的判断顺序应该是先测量再动手:用 React DevTools 的 Profiler 面板录一次交互,看火焰图里哪些组件的实际渲染时间最长、渲染原因是什么,再决定在哪一层下手。盲猜热点经常猜错,一次列表渲染慢,罪魁可能是某个子组件里的昂贵计算,也可能是整棵子树被无关 state 拖着重渲染,对应的解法分别指向 useMemo 和 React.memo。

行业风向也在变化。2025 年 10 月发布的 React Compiler 1.0 已经可以在编译期自动分析组件并插入 memoization,组件、值、回调的大部分手工优化都不再必要,Next.js 16 中通过配置项即可开启。Meta 已在 Facebook 和 Instagram 的生产代码上落地这套自动方案。这意味着新项目里手工写 React.memo 和 useMemo 更多是兜底手段:编译器覆盖不到的场景、或者尚未接入编译器的存量代码,仍靠人工优化撑住性能。

适用场景对比:什么情况用哪一个

props 在父组件重渲染时基本不变的展示型组件,用 React.memo;渲染期间存在昂贵计算且依赖清晰的,用 useMemo。四个典型场景的归属如下:

场景 优先使用 原因
纯展示型子组件、props 引用稳定 React.memo 跳过整棵子树的渲染
大数组的过滤、排序、聚合统计 useMemo 计算成本高、依赖明确
传给 memo 子组件的回调函数 useCallback 配合 稳定引用,否则 memo 失效
作为其他 Hook 依赖的对象或数组 useMemo 避免引用变化触发 effect 反复执行
props 每次都是新建的对象或函数 两者都无效 先解决引用稳定问题再谈优化

落到操作层面,可以按五个步骤走一遍:

  1. 打开 Profiler 录制一次典型交互,定位渲染时间最耗时的组件;
  2. 确认该组件 props 引用稳定后,用 React.memo 包裹导出;
  3. 把组件内昂贵的过滤、排序计算收进 useMemo,依赖数组写全;
  4. 传给 memo 子组件的回调用 useCallback 固定引用;
  5. 复测性能,确认优化收益大于比较开销,没有收益就回退。

流程里最容易漏的是第 5 步。包裹之后不复测,优化就停留在「感觉变快了」的层面,而比较开销是真实发生的,子组件数量一多,省下的渲染时间和花掉的比较时间可能就在互相抵消。

常见误用与排查思路

从排查经验看,失效案例集中在三处。其一,给 memo 组件传属性时顺手写了内联对象(比如 style={{ marginTop: 8 }})或内联函数,浅比较每次都不相等,包裹完全不起作用,控制台里子组件照样渲染;其二,把 useMemo 当成语义保证,在计算函数里夹带副作用或依赖「只算一次」的假设,一旦 React 丢弃缓存重新执行,行为就对不上了,副作用应该交给 useEffect,只算一次的初始化应该交给 useState 的惰性初始化;其三,无差别包裹,每个变量都套 useMemo、每个组件都套 memo,代码噪音上升,内存多占了一份,真正需要优化的地方反而被淹没。排查时先看渲染原因标签,再检查 props 引用来源,两步就能定位大多数问题。

常见问题(FAQ)

Q1:React.memo 和 useMemo 到底怎么选?

组件重渲染贵、props 稳定,选 React.memo;计算贵、依赖明确,选 useMemo;两者经常配合使用。

Q2:为什么包了 React.memo 子组件还是重渲染?

大概率 props 引用每次都变:内联对象、内联函数让浅比较失效,先用 useCallback 或模块级常量稳定引用。

Q3:useMemo 能不能把所有变量都包一层?

不能。依赖比较与缓存本身有开销,滥用推高内存占用、降低可读性,先测量再包裹。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
小码农的头像小码农认证作者

相关推荐

返回顶部