60 秒回答模板

把所有组件都用 React.memo 包起来通常不是好做法。memo 的收益来自 props 没变时跳过渲染,但它本身要做 props 浅比较。如果组件很轻、props 经常变化、父组件每次传新对象或新函数,memo 很难命中,比较成本和代码复杂度反而增加。为了让 memo 生效,开发者还可能到处加 useMemo/useCallback,带来依赖维护、闭包旧值和可读性问题。更合理的做法是用 Profiler 找到渲染频繁且耗时的组件,再针对稳定 props 的重组件局部使用 memo。

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

深入解析

01

memo 的收益条件

memo 只有在子组件渲染成本较高,并且 props 在很多次父渲染中保持浅比较相等时才有明显收益。轻组件或 props 总变化的组件,跳过机会少。

02

比较本身有成本

父组件每次渲染时,memo 组件都要做一轮 props 比较。props 很多、比较函数复杂或组件本身很轻时,比较成本可能接近甚至超过重新渲染成本。

03

引用不稳定会让 memo 失效

对象字面量、数组字面量、内联函数每次 render 都会产生新引用。即使内容一样,浅比较也认为变化,memo 无法跳过渲染。

04

连锁引入复杂度

为了稳定引用,团队可能大量添加 useMemo/useCallback。这样依赖数组变多,漏依赖会产生旧闭包,多写依赖又让缓存频繁失效,代码阅读成本上升。

05

可能掩盖架构问题

过度 memo 有时是在掩盖状态放得太高、组件边界太粗、列表项过重、上下文频繁变化等问题。真正的优化可能是状态下沉、拆分 context、虚拟列表或减少派生计算。

06

优化流程

先用 Profiler 看哪些组件渲染频繁、耗时高、为什么渲染;再选择 memo、稳定 props、拆组件或调整状态结构。优化后再次测量,确认收益大于成本。

易错点

  • 认为 memo 没副作用,可以作为所有组件默认模板。
  • 忽略父组件传入新对象、新函数导致 memo 每次失效。
  • 为了命中 memo 到处漏写 Hook 依赖,制造旧闭包问题。
  • 没有用 Profiler 证明组件重渲染真的昂贵。

面试官追问

什么组件适合 memo?

渲染成本较高、props 大部分时间稳定、父组件频繁渲染、且跳过渲染确实能带来收益的纯展示组件更适合。

context 更新会不会绕过 memo?

组件内部读取的 context 变化时仍会重新渲染,即使 props 没变。可以拆分 context、选择器化读取或把读取 context 的部分和重组件分开。

自定义比较函数能解决所有问题吗?

不能。比较函数太深或太复杂会很贵,还可能因为比较不完整导致 UI 不更新。通常要谨慎使用。

除了 memo 还可以怎么优化?

可以状态下沉、拆分组件、拆分 context、虚拟列表、减少派生计算、延迟非关键渲染、缓存昂贵计算和优化数据结构。