先记住这个答案
memo 会增加输入比较和维护复杂度,只有它经常命中、且跳过的子树工作足够昂贵时才更可能有收益。输入每轮都真实变化、存在总是新建的对象 prop、子树很便宜或比较器扫描大量数据时,优化可能无效甚至更慢。本地状态和 Context 造成的更新也不会因为 props memo 自动消失。应先定位真实慢交互,比较相同负载下的用户延迟和组件工作量,再决定保留哪些边界。
- 比较成功率和节省的工作要一起看
- 深比较也占用主线程时间
- 状态位置与订阅范围常比加缓存更有效
每次都变化的输入没有多少可复用结果
一个受控输入框接收正在输入的 value,每次按键都需要展示新字符。在这个输入边界上加 memo,不能合理地跳过 value 的真实变化;若卡顿来自旁边的大列表,应把分析范围移到列表及其数据依赖。
如果慢列表接收一个每次都新建的 options 对象,先判断它的内容是否真正变化。无关重建可以调整接口或缓存引用,真实变化则必须传播。比较器忽略变化以提高命中率,可能把性能问题变成陈旧界面。
比较可能比重新生成界面更贵
假设一个只显示标题的子组件,比较器却递归扫描整份业务记录。即使最后判断相等,扫描、属性访问和临时分配也已经发生;数据规模增大时,这段同步比较本身可能阻塞输入响应。
不要只比较加 memo 前后组件函数的日志条数。应记录同一份数据、相同操作序列和相近设备条件,观察交互耗时及渲染开销,并区分开发检查带来的额外调用。生产分析需要使用合适的性能工具和构建条件。
优先修正状态传播和慢路径
表单草稿如果只在一个输入区使用,就不必提升到管理整页的祖先。局部状态可以缩小更新传播范围;主题、会话与高频编辑状态混在同一个 Context 时,也应检查是否能拆分消费来源。
对于确实昂贵的筛选、排序或长列表,分别考虑计算复用、减少展示数量或虚拟化。memo 只处理组件复用边界,不会自动把大计算移到后台,也不会减少首次必须展示的全部节点数量。
容易答错的地方
- 把零次额外渲染当作最终目标
- 一些重新渲染成本很低,用户也感知不到。为了消除它们增加多个缓存和比较器,会让依赖更难维护;应围绕可测量的交互问题优化,并保留说明触发条件和收益的证据。
- 在开发日志里宣布性能提升百分比
- 严格模式、调试扩展、热更新和日志本身都可能改变执行成本。开发记录可以定位疑点,但量化收益应明确环境和操作样本,重复对照并报告波动,不能把单次耗时当作稳定结论。
面试官还会怎么问?
是不是所有简单组件都应该移除 memo?
也不能一概而论。简单组件可能位于非常高频的路径,已有稳定输入与大量实例也会影响总成本。应以实际测量决定,避免仅凭代码行数或统一风格要求,批量加入或删除性能边界。
使用 React Compiler 后还需要手写吗?
实际启用编译器后,它能自动进行多种缓存优化,手写 memo 的需求可能减少。是否保留现有边界要看编译覆盖和实测结果;没有启用编译器的项目不能假设这些优化已经自动存在。
如何设计一个有说服力的对照实验?
固定数据量、设备与操作序列,分别测试保留和移除边界的版本,确认输出和交互行为一致。记录慢交互、子树工作量及比较成本,重复多次观察分布,再说明收益发生在哪类输入变化下。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。