React 中的记忆化:或者说我以为自己优化了应用(但大多只是觉得自己很有成就感)
看起来您只提供了来源链接,而没有贴出需要翻译的正文内容。请把您想要翻译的文本(包括 Markdown 格式)粘贴在这里,我会按照要求把它翻译成简体中文并保留原有的格式。
“性能阶段”。
你不再担心让东西能工作,而是开始担心让东西快速工作。你开始像他们欠你钱一样盯着重新渲染,打开 React DevTools 的频率比打开 Slack 还高,最后在 Google 上搜索:
“如何防止 React 中的重新渲染?”
React 以它的无限智慧,给出了三个魔法词:
useMemouseCallbackmemo
你阅读了几篇博客文章,以 1.25 倍速观看了 YouTube 视频,在代码里像加调味料一样撒上一点记忆化,然后——voilà——✨ 已优化。✨
或者……你是这么认为的。
那就是我——很长一段时间。我觉得自己写的 React 已经很高性能,但我决定不再凭感觉,而是观察 React 在几种情形下的实际表现。结果让我谦卑:我一直在以看似有效的方式“优化”我的应用,却并没有达到预期的效果。
今天我将带你了解一些关于 React 中记忆化的并不明显的误区,并使用这个仓库作为我们的实验场:
👉 Repo: (link omitted in original)
如果你对任何内容有异议——或者有什么点拨到你了——请在评论里(礼貌地)大声说出来。
在我们变得花哨之前,先达成一些事实
React 组件在以下情况下会重新渲染:
- 内部状态改变
- 父组件重新渲染
有些人会再加一条规则:
- 属性(props)改变
个人认为第 2 条已经涵盖了这点。如果你不同意,欢迎在评论区与我争论,我持开放态度。😄
记住这两条规则——我们会像讲师一次又一次指着同一张幻灯片说“这会出现在考试里”一样反复提到它们。
误解 #1
“将 memo 化的值传入组件可以防止重新渲染”
简短回答: 不会。
详细回答: 让我来说明我为什么也会陷入这个误区。
切换到仓库的 without-memoized-component 分支。你会看到两个组件:
memoized-component.tsx

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

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

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

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