60 秒回答模板

React.memo 是包在组件外层的高阶组件,默认对 props 做浅比较,props 没变时跳过子组件重新渲染。useMemo 是在组件内部缓存一个计算结果,依赖不变时复用上次返回值,适合昂贵计算或需要稳定对象引用的场景。useCallback 缓存的是函数引用,本质可以理解为返回函数的 useMemo,常用于把回调传给 memo 子组件或作为 effect 依赖。三者都不是免费优化:要维护依赖、做比较和占用内存,只有当跳过渲染或计算的收益大于这些成本时才值得用。

考点 核心机制与工程取舍
难度 中高频面试题
回答目标 按定义、机制、场景讲清楚

深入解析

01

React.memo 作用在组件层

memo 包住函数组件后,父组件渲染时 React 会比较新旧 props。默认是浅比较,引用类型只比较引用是否相同。命中时跳过该子组件本次渲染。

02

useMemo 作用在值层

useMemo(() => compute(), deps) 会在依赖不变时复用上次计算结果。它适合昂贵计算、筛选排序大数组、稳定对象引用和避免下游 memo 组件因新对象失效。

03

useCallback 作用在函数引用

useCallback(fn, deps) 返回稳定的函数引用,依赖变化时才更新。它常用于把回调传给 memo 子组件、传给自定义 Hook 或避免 effect 因函数引用变化重复执行。

04

三者需要配合才有意义

如果子组件没有 memo,父组件每次渲染时稳定 callback 未必带来收益。如果 memo 子组件接收的对象或函数每次都是新引用,memo 也很难命中。优化要看整条 props 链。

05

成本和依赖风险

浅比较有成本,useMemo/useCallback 有依赖维护成本,也可能因为漏依赖产生闭包旧值。过度使用会让代码更难读,却未必减少实际渲染。

06

正确使用顺序

先用 React DevTools Profiler 找出频繁渲染且代价高的组件,再用 memo、useMemo、useCallback 局部优化。不要把它们当成默认模板。

易错点

  • 把 memo、useMemo、useCallback 都说成缓存组件,不区分组件、值和函数。
  • 认为 useCallback 能阻止函数执行,实际上它只稳定函数引用。
  • 无脑包所有计算和函数,忽略依赖维护和比较成本。
  • 为了让 memo 命中漏写依赖,导致闭包旧值和难查 bug。

面试官追问

useCallback 和 useMemo 有什么关系?

useCallback(fn, deps) 可以理解为 useMemo(() => fn, deps),只是语义上专门用于缓存函数引用。

React.memo 默认怎么比较 props?

默认浅比较。基础类型比值,引用类型比引用地址。可以传自定义比较函数,但比较逻辑太重可能比重新渲染还贵。

什么时候 useMemo 不值得用?

计算很轻、依赖经常变化、结果不传给 memo 子组件、或者只是为了包一层普通对象时,收益通常不明显,还会增加复杂度。

漏写依赖会有什么问题?

可能读到旧 props 或旧 state,表现为回调和计算结果滞后。依赖数组不是性能开关,而是声明闭包使用了哪些外部值。