React.memo vs useMemo

深入理解React优化策略的真相:它们不是在"优化渲染",而是在"延迟计算代价"

一个被误解的真相

React.memo 和 useMemo 主要优化的是"不同的东西",很多开发者误用它们,导致"优化"反而适得其反。

React.memo

主要优化组件级别的渲染,避免整个组件因为父组件的重新渲染而不必要地更新。

适合用于接收相同 props 的组件,避免无意义的重新渲染

如果 props 变化频繁,它的作用就很有限,甚至可能有额外的对比开销

useMemo

主要优化计算级别的开销,避免在组件内部重复执行昂贵的计算。

适合计算开销大且依赖项不经常变动的情况,避免重复计算

如果计算本身很轻量,useMemo 反而会因为缓存带来额外开销

代码示例与实践

React.memo 示例

适用于纯展示组件(Pure Component),当父组件的 props 传入没有变化时,就不会重新渲染。

const ExpensiveComponent = React.memo(({ data }) => {
  console.log('渲染了 ExpensiveComponent');
  return <div>{data.value}</div>;
});

注意: React.memo 不会减少渲染,只是减少了组件更新。如果 props 频繁变化,它反而会增加额外的 props 对比开销。

useMemo 示例

适用于计算昂贵的逻辑,比如复杂计算、数组过滤等。

const filteredList = useMemo(() => {
  console.log('计算了 filteredList');
  return list.filter(item => item.isActive);
}, [list]);

注意: useMemo 并不会"记住"整个值,而是只缓存计算结果,如果计算开销不大,它的缓存反而会带来不必要的性能损耗。

深入理解:延迟计算代价

React.memo 和 useMemo 不是在"优化渲染",而是在"延迟计算代价"——它们并不会让 React 变快,而是让 React 在某些情况下"不做事"。

误区:React.memo / useMemo 可以提升 React 组件的性能

很多人以为 React.memo 和 useMemo 是让组件跑得更快,但实际上,它们只是换了一种时间分配策略:

  • React.memo 只是推迟重新渲染,而不是让渲染变快。它的本质是在 React 需要重新渲染时,额外做一次 props 浅比较,如果 props 没变,就跳过渲染。这意味着,如果 props 变化频繁,React.memo 的浅比较反而会增加开销,导致 React 并没有变快,甚至更慢了。
  • useMemo 只是推迟重新计算,而不是让计算变快。它本质上是在依赖项不变时复用之前的计算结果,但 React 仍然要维护这部分缓存的生命周期,这意味着如果计算本身很轻量,useMemo 反而会带来额外的内存和对比开销。

React.memo 性能权衡

useMemo 性能权衡

真相:React.memo / useMemo 本质上是"权衡计算时机",而非"性能优化"

1. React.memo 让 React 选择"等一下"

  • 如果组件更新代价大于浅比较代价(即 props 变化少但渲染成本高),那么 React.memo 会带来优化。
  • 但如果组件更新代价小于浅比较代价(即组件本身渲染很快,但 props 变化频繁),React.memo 反而会拖慢 React。

2. useMemo 让计算"换个时间发生"

  • 如果计算开销大,且依赖项变化少,useMemo 会延迟计算成本,在下一次依赖变化时才重新计算。
  • 但如果计算开销小,React 维护 useMemo 的缓存反而会带来额外的管理成本,使性能更低。

关键点与最佳实践

关键点:改变"工作安排"而非减少"工作量"

React.memo / useMemo 不是减少 React 的"工作量",而是改变"工作安排"。如果你误以为它们会让 React 执行得更快,你可能会错误地滥用它们。

React.memo 适用场景

"昂贵组件"(渲染成本远大于 shallowEqual props 检查的成本)

useMemo 适用场景

"昂贵计算"(计算成本远大于缓存的管理成本)

如果组件本身很轻量,或者计算成本不高,使用它们反而会拖累 React!

最佳实践建议

先测量,再优化

在应用 React.memo 或 useMemo 之前,先使用 React Profiler 或性能工具确认瓶颈所在。

考虑组件设计

有时候,重新设计组件结构比添加记忆化更有效。考虑将大型组件拆分为更小的组件。

避免过早优化

不要在没有性能问题的地方预先添加 React.memo 或 useMemo。过早优化可能会增加代码复杂性而没有实际收益。

理解依赖项

确保正确设置 useMemo 的依赖数组,避免遗漏依赖导致缓存失效或包含不必要的依赖导致过度计算。

终极启示

React 不是在变快,而是在选择"什么时候做事"

React.memo 和 useMemo 不会让你的组件运行得更快,它们只是试图减少不必要的工作。但如果这个额外的"减少"本身需要付出很高的代价,那就变得得不偿失。

所以,与其想着"优化渲染",不如思考"哪里真正需要优化"——有时候,最好的优化策略是让 React 自由地重新渲染,而不是人为干预它的渲染逻辑。

进一步阅读

React设计模式与最佳实践

作者:Carlos Santana Roldán

深入探讨React组件设计模式,包括性能优化策略和记忆化技术的最佳实践。

深入浅出React和Redux

作者:程墨

从基础到高级,全面讲解React生态系统,包括性能优化和Hooks的正确使用方式。

React性能优化指南

作者:Ivan Akulov

专注于React应用性能优化的实用指南,包含大量实际案例和性能测量方法。

React官方文档:记忆化

React团队

官方文档中关于React.memo、useMemo和useCallback的详细解释和最佳实践指南。

《你可能不需要派生状态》

React团队博客

探讨React中状态管理和计算属性的最佳实践,与useMemo的使用密切相关。