React的“主要工作”是保持应用UI与React状态同步。重新渲染的关键在于找出需要改变的地方。
我强烈建议你在低端设备上测试你的应用,这样你才能了解90 分位用户的真实体验是什么样的。
具体用什么设备取决于你所构建的产品,但就我写这篇博客来说,我会定期在一台 小米 Redmi 8 上测试——这是一款几年前在印度非常流行的预算智能手机。
Lighthouse 的性能评分并不能准确反映真实的用户体验。
比起任何自动化工具所显示的统计数据,我更信赖亲身使用应用时的主观体验。
我几年前在 React Europe 做过一次关于 React 性能的演讲!
那次演讲主要关注的是“页面加载后”的性能表现,这是很多开发者容易忽视的领域。你可以在 YouTube 上观看那次演讲(链接可打开新标签)。
不要过度优化!
在学习 React Profiler 时,你可能会有冲动:想要一口气优化整个项目,尽可能减少组件的渲染次数……
但说实话,React 本身就已经非常优化了。
这些工具最适合用在真正遇到性能问题时,比如当应用开始变得卡顿、不流畅的时候,再使用它们去定位问题和优化。 推荐这个人的播客: www.joshwcomeau.com/
在 React 中,组件的重新渲染(re-render)通常由父组件更新、props 改变、state 改变或 context 值变化触发。但是否真正发生 re-render,还取决于变更内容是否真的不同,以及是否使用了优化手段。例如,若父组件更新但子组件使用了 React.memo 且 props 未变化,子组件就不会重新渲染;类似地,调用 setState 但新旧值相同,也不会触发渲染。对于 context,任何消费该 context 的组件在值变化时默认都会 re-render,但可以通过 context 拆分、选择性订阅等方式减少渲染范围。因此,在进行性能优化或构建大型系统架构时,理解这些渲染触发机制及优化策略尤为关键,能有效避免不必要的渲染,提升整体性能。
在理想的情况下,React 组件应该总是“纯的”(pure)。一个“纯组件”指的是:只要 props 一样,它就总是渲染出相同的 UI。
但在现实世界里,很多组件都是不纯的(impure) 。
function CurrentTime() { const now = new Date(); return ( <p>It is currently {now.toString()}</p> ); }
这个组件每次渲染时都会显示不同的时间,因为它依赖的是当前时间 new Date()!
更隐蔽的问题是:ref 的使用也会导致类似情况。当我们把一个 ref 作为 prop 传递时,React 无法判断这个 ref 是否被修改过,于是它为了保险起见,就选择重新渲染。
✅ 什么是纯组件?
“纯组件”指的是:在相同的 props 下,总是渲染出相同的 UI,且不依赖副作用或外部可变数据。
也就是说:只要 props 没变,就不需要重新渲染。
✅ React.memo 是干什么的?
React.memo 是 React 提供的高阶组件,用来缓存组件的渲染结果。它的行为类似于类组件中的 PureComponent。
既然Reac.memo是我们想要的,为什么react不默认用这种方式进行比较?
React 不默认使用 React.memo 进行浅比较主要有几个原因:
性能权衡考虑 浅比较本身也有开销。对于简单组件或经常变化的 props,进行比较的成本可能比直接重新渲染更高。React 团队认为让开发者根据具体场景选择是否优化更合理。
避免意外的 Bug 浅比较可能导致一些不易察觉的问题:
- 当 props 是对象或数组时,如果内部值变化但引用没变,组件不会更新
- 开发者可能忘记这种行为,导致 UI 不同步
保持简单的心智模型 React 的默认行为是"props 变化就重新渲染",这很直观。如果默认启用 memo,开发者需要时刻考虑浅比较的语义,增加了复杂性。
鼓励更好的架构 React 更希望开发者通过合理的组件设计、状态管理来避免不必要的渲染,而不是依赖自动优化。过度依赖 memo 可能掩盖架构问题。
向后兼容 改变默认行为会破坏现有应用的行为,这是 React 团队非常谨慎避免的。
实际上,React 18+ 的并发特性和自动批处理已经在底层做了很多优化,很多情况下手动 memo 的收益在减少。React 团队的哲学是让开发者在真正需要时才显式添加优化。
在“纯组件(pure components)”的语境下,可以把 Context 看作是“看不见的 props”或者说“内部 props”。
注意:只有当这个纯组件主动调用了 React.useContext 来消费某个 Context 时,才会有这种行为。
你不需要担心 Context 会影响那些根本没使用它的纯组件,它们不会因为 Context 的更新而莫名其妙地重新渲染。