loader

Nerio News Magazine brings you trusted, timely and thought-provoking stories from around the globe.

Follow Us

React 19.2 编译器时代:useMemo、useCallback 还要不要手写

Share This Article:

React 19.2 之后 React Compiler 自动完成记忆化,useMemo/useCallback 是否该全删?本文讲清编译器的工作边界、三类仍需手写记忆化的场景,以及老项目渐进接入的可执行步骤。

React 19.2 编译器时代:useMemo、useCallback 还要不要手写
关键词React 19、React Compiler、useMemo、useCallback、自动记忆化、前端性能优化

React 19.2 之后,React Compiler 正式进入稳定可用阶段。它带来了一个足以改变日常编码习惯的能力:自动记忆化(auto-memoization)。于是社区里最常被问的一句话变成了——useMemouseCallbackReact.memo 是不是可以全删了?

答案是:大部分可以删,但有几种情况必须保留。本文讲清编译器的工作边界、三类仍然需要手写记忆化的场景,以及老项目渐进接入的可执行方案。

一、版本坐标

截至 2026 年 9 月,reactreact-dom 的最新版本为 19.2.8。React Compiler 自 19.2 起可以作为稳定能力使用,配套的代码检查由 eslint-plugin-react-hooks 提供。版本信息可随时在 npm 上的 react 包页面核对。

二、React Compiler 到底做了什么

一句话概括:编译器在构建期分析组件的数据流,自动插入等价的记忆化代码,让组件在 props 或状态没有本质变化时跳过重渲染。

它分析的核心问题是——哪些值的变化会真正影响输出。对于纯计算、字面量、以及依赖链清晰的值,编译器能安全地推导出缓存边界;对于它无法证明安全的情况,会选择 bail out(放弃优化),退回普通重渲染,而不是给出错误结果。

React Compiler 的工作链路
React Compiler 工作链路:编译期分析数据流,自动插入等价的记忆化代码

理解这一点很重要:编译器优先保证正确性,其次才是性能。所以”开了编译器就一定更快”是误解,它只是把手写记忆化里大量机械、易错的部分自动化了。

三、三种写法的对比

以”过滤一个大列表”为例。

1. 完全手写(编译器时代之前)

function List({ items, keyword }) {
  const filtered = useMemo(
    () => items.filter(i => i.name.includes(keyword)),
    [items, keyword]
  )
  const onSelect = useCallback((id) => { /* ... */ }, [])
  return <Table data={filtered} onSelect={onSelect} />
}

export default React.memo(List)

问题在于依赖数组靠人维护:漏写会拿到过期值,多写会让缓存失效,而 onSelect 这类回调经常因为”忘了包一层”导致子组件天天重渲染。

2. 编译器接管后

function List({ items, keyword }) {
  const filtered = items.filter(i => i.name.includes(keyword))
  const onSelect = (id) => { /* ... */ }
  return <Table data={filtered} onSelect={onSelect} />
}

代码回到最自然的样子,缓存由编译器插入。可读性和正确性的收益立竿见影——尤其是新人不理解依赖数组语义时造成的隐性 bug。

3. 仍然需要手写的场景

下面三类,编译器帮不上忙或者不该帮:

  • 依赖了编译器看不到的外部值:例如 ref.current、模块级可变单例、DOM 直接读取。编译器无法追踪这些值的变化,缓存可能导致读到脏数据;
  • 需要稳定引用语义的对外契约:把函数作为长期订阅的回调传给第三方库(事件总线、WebSocket、原生监听器),这些库内部可能按引用做增删,引用变化会引发反复解绑重绑;
  • 计算极其昂贵且命中率高:例如数万行数据的聚合、复杂图表布局计算。编译器按数据流分析,未必能识别出”这个值值得跨多次渲染缓存”的业务语义,此时显式 useMemo 依然是明确且可控的表达。

四、工程落地:老项目怎么接

  1. 先上 lint,再上编译。eslint-plugin-react-hooks 的编译器规则跑起来,它会告诉你哪些组件被 bail out 以及原因,这是最直接的体检报告;
  2. 按目录渐进开启。 优先在交互密集、重渲染明显的模块(表格、编辑器、看板)开启,别一次性全量;
  3. 清理要有顺序。 先删 React.memo 和纯机械包装的 useCallback,再评估 useMemo;删完用 React DevTools Profiler 对比关键路径的重渲染次数;
  4. 把性能写进测试。 对核心页面做一次交互耗时的基线采集,接 CI 回归,避免”优化”变成回归。

判断标准不是”代码里还有没有 useMemo”,而是一次交互里发生了多少次不必要的重渲染。用 Profiler 的数字说话,比争论风格有意义。

五、小结

React Compiler 把”记忆化”从开发者手动维护的纪律,变成了编译器保证的默认行为。这不是让 useMemo 消失,而是让它回归本职:只在编译器无法推断、或业务语义需要显式表达时使用。

对新项目,建议从一开始就按编译器的心智模型写组件——保持数据流清晰、避免在渲染中读取可变外部状态。对老项目,按模块渐进接入,用 Profiler 数字验证收益,别为了”看起来现代”做全量重构。


延伸阅读:React 官方文档 · React CompileruseMemo API 参考useCallback API 参考。版本信息采集于 2026 年 9 月 1 日。

标签

#React#前端性能优化#编译器#前端工程化

Related Post

发表回复

Your email address will not be published.