React 中的 Memoization:或者说我以为我优化了我的应用(但大多只是感觉自己很有成效)
Source: Dev.to
请提供您希望翻译的完整文本内容(文章正文),我会将其准确地翻译成简体中文并保留原有的 Markdown 格式、代码块以及技术术语。谢谢!
“性能阶段”。
你不再担心让东西能运行,而是开始担心让它们运行得 快。
你开始对重新渲染侧目而视,好像它们欠你钱一样,打开 React DevTools 的频率比打开 Slack 还高,最终去 Google 搜索:
“How to prevent re‑renders in React?”
React 以其无尽的智慧,给出了三个魔法词:
useMemouseCallbackmemo
你阅读几篇博客文章,以 1.25× 速率观看 YouTube 视频,在代码里撒上一点 memoization,就像调味料一样,然后——voilà——✨ 已优化。 ✨
或者……你这么认为。
那就是我——很长一段时间的我。我 感觉 自己在写高性能的 React,但我决定不再只凭感觉,而是 观察 React 在几种情境下的实际表现。结果让我谦卑:我一直在以看似有效的方式“优化”我的应用,却并没有实现我以为的效果。
今天我将带你了解一些 关于 React 中 memoization 的不太明显的误区,并使用这个仓库作为我们的练习场:
👉 Repo:
如果你对任何内容有异议——或有共鸣——请在评论中(礼貌地)大声说出来。
在我们深入之前,让我们先达成一些共识
当以下情况发生时,React 组件会重新渲染:
- 它的 内部状态改变
- 它的 父组件重新渲染
有些人会添加第三条规则:
- 它的 属性(props)改变
个人认为第 2 条已经涵盖了这一点。如果你不同意,欢迎在评论中与我争论。我持开放态度。 😄
记住这两条规则——我们会像讲师一次又一次指着同一张幻灯片并说 “这会出现在考试里” 那样反复提到它们。
误解 #1
“将记忆化的值传入组件可以防止重新渲染”
简短回答: 不会。
详细回答: 让我来说明我为什么也会陷入这个误区。
切换到仓库中的 without-memoized-component 分支。你会看到两个组件:
memoized-component.tsx

(假装我在戏剧性地指着它)
App.tsx

(若有所思地点头)
注意 App.tsx 中的一点重要内容:我们在把函数作为 prop 传下去之前使用了 useCallback。此时,过去的我已经在庆祝了。
“函数已经被记忆化。React 不会重新渲染子组件。我是性能天才。”
现在点击 count 按钮:

子组件 每一次 都会重新渲染。
“但是函数没有变化!”
没错。金星 ⭐ 函数引用是稳定的。
但请记住规则。回到规则 #2:当 父组件重新渲染 时,子组件也会重新渲染。父组件的状态变了,所以父组件重新渲染,子组件随之重新渲染。
useCallback 并没有让你失望——是你的期望失望了(我也是)。在这种情况下,useCallback 基本上是情感支持:它让你 感觉 更好,却并没有真正阻止任何事情。
“那……我们该怎么解决?”
很简单。给子组件加上 memo 包装,然后刷新应用(真的刷新),再点击按钮:

不再重新渲染。现在我们实现了之前以为已经实现的效果。
教训: 仅仅记忆化 props 而不记忆化组件本身 对重新渲染毫无帮助。
误解 #2
“如果我对组件使用 memo,我就安全了”
memo 只在 属性未改变 时阻止重新渲染。React 使用 浅层比较 来检查这一点。因此,如果你做了以下任意操作,React 会看到一个新的引用并重新渲染:
- 传入内联函数
- 传入新创建的对象
- 传入未被 memo 化的派生值
此时,你的 memo 包装器仅是装饰性的。
你可以自己测试:
- 从
App.tsx中移除useCallback - 点击按钮
- 观察渲染计数像在做有氧训练一样上升
痛苦的真相
在 未对传入的值进行 memo 化 的情况下对组件进行 memo 化,就像锁上前门却把窗户敞开一样。看起来很安全——其实并不是。
结论
了解 何时 以及 什么 需要进行记忆化至关重要。使用 useCallback/useMemo 来保持 prop 引用的稳定,并且仅在确信 props 在渲染之间真正保持不变时才用 memo 包装 组件。
记忆化有:
- 认知成本
- 维护成本
- 有时甚至会带来性能成本
它只有在以下情况下才会发挥作用:
- 重新渲染代价高
- props 稳定
- 你已经测量到真实的问题(而不仅仅是感觉)
我将在另一篇文章中更深入探讨 何时 记忆化真正有意义。
最后的思考(来自亲身经历的教训)
很长一段时间,我以为自己写的 React 性能很好,因为我用了正确的工具。
我没有做到的是:
- 观察行为
- 质疑假设
- 理解 为什么 React 会重新渲染
记忆化并不是魔法。
它只是一个工具。
和所有工具一样,只有真正了解它的工作原理时,它才能发挥最佳效果。
如果我有什么说错的、误解的,或者这篇文章让你对记忆化有了不同的认识,欢迎留言。我真的很想听听你的想法。
下篇再见 👋