React Compiler 1.0 发布半年后的现状

去年 10 月,React 官方发布了 React Compiler 1.0 稳定版。

现在半年过去了,真放到生产项目里跑了几轮,有些感受跟当初想的完全不一样。今天把真实体验摊开说说,哪些是真香,哪些是坑。

先说说半年后的生态现状

现在 React Compiler 的覆盖情况是这样的:

  • React 19 原生支持,新项目默认就用上了
  • Next.js 15.3.1+ 稳定支持,Next.js 16 开箱即用
  • Expo SDK 54+ 默认已开,React Native 也能享受到
  • Vite 8 的集成出现了新变化

这里面有个坑值得单拎出来说。

Vite 8 的配置变天。

@vitejs/plugin-react v6 做了个大动作:把内置的 Babel 换成了 oxc(Rust 实现的解析器)。JSX 转译和 Fast Refresh 全走 Rust,编译速度确实快了很多,但代价是旧的 react({ babel: { plugins: [...] } }) 写法直接废了。

现在要跑 React Compiler,得外挂 @rolldown/plugin-babel

javascript 复制代码
// vite.config.js - 2026年新写法
import { defineConfig } from 'vite'
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import { babel } from '@rolldown/plugin-babel'

export default defineConfig({
  plugins: [
    babel({
      include: /\.[jt]sx?$/,
      babelConfig: reactCompilerPreset(),
    }),
    react(),
  ],
})

网上一大半教程还在教老写法。你要是抄到 Vite 8 的项目里,build 直接崩。注意 babel() 必须在 react() 之前。

对于还没升 Vite 8 的项目,旧写法继续用就行,不冲突。

Next.js 16 开箱自带。

这是体验最好的:装好就是开的,零配置。而且 Next 15.3.1+ 也可以用 experimental.reactCompiler 打开,门槛极低。

真正有用的地方,和当初说的不一样

最大的收益不是性能,是认知

这是最出乎意料的。

半年前所有人都在吹"编译器帮你省 useMemo",但实际跑下来,性能提升在大部分场景下并不明显。页面该 60fps 的还是 60fps,该卡的地方不是因为没写 memo,而是因为代码本身设计有问题。

编译器真正改变的是 写代码的心态

以前写一个组件,脑子里要同时想两件事:业务逻辑对不对 + 这地方要不要加 memo。每写一个计算就得犹豫一下。code review 里周期性地出现"这里加个 useCallback 吧"的 comments。

现在不用想了。先写干净的代码,编译器兜底。那种"写代码之前先算依赖"的 mental overhead 消失了。这个体验的提升比帧数提升更实在。

InfoQ 在 2025 年底的报道里提到,Meta 内部跑了 React Compiler 一年多,反馈也是类似的:最大的收益不在 benchmart 数字,而在团队认知负担的减轻。

粒度比你手写精细

Sascha Becker 在 18 个月回顾文章里提到一个细节:你手写 useMemo 的时候,只能对整个计算对象做缓存。编译器能做到的是对子表达式级别做缓存。

举个例子,你手写一般是这样的:

javascript 复制代码
const formatted = useMemo(
  () => ({
    label: formatLabel(entity),
    total: entity.items.reduce(...),
    status: entity.state === "active" ? "green" : "red",
  }),
  [entity]
);

如果只是 entity.state 变了而 items 没变,整个 formatted 还是得全算一遍。编译器的粒度更细,只会重新算变化的部分。

这种细节层面的优化,大多数场景不会让你有感知,但在复杂列表渲染的场景下,比如一个列表项里有多个派生数据,确实能看到渲染树的缩小。

那些编译器搞不定的地方

老项目别急着打开

Reddit 上一个跑生产 6 个月的经验帖提到的最重要教训是:React Compiler 最难的不是装上去,是装上去之后

打开编译器,CI 过了,跑起来也没报错,然后你发现某些组件莫名其妙地多渲染了几次。因为编译器对不符合 "Rules of React" 的代码会静默跳过,而不是给你报错。

哪些代码会触发静默跳过?

  • render 里改了 props 或闭包变量。很多人嘴上说"不这么干",但拷问细节就露馅了。
  • render 里读 ref.current。这是个很常见的操作:取 DOM 尺寸、判断元素状态,编译器直接跳过你的组件。
  • 修改入参默认值
javascript 复制代码
function Field({ value = defaultValue }) {
  value = value ?? fallback;  // 编译器跳过
  // ...
}
  • async 函数。组件里用 async 的也是跳过。

每种跳过都很安静。你不会收到 error,只是在 React DevTools 里看不到那个代表已优化的 ✨ 徽章。

解法:装 eslint-plugin-react-compiler,把 react-compiler/react-compiler 规则设成 error。在 CI 里翻成 error,没通过就不允许合并。这是唯一靠谱的做法。

逃生阀 "use no memo"

如果编译器真的对一个复杂组件判断错了,或者迁移成本太高,可以在函数顶部加一行:

javascript 复制代码
function VeryComplicatedThing() {
  "use no memo";
  // 编译器跳过这个函数
}

但这东西最好别变成习惯。有团队吐槽这成了他们的永久技术债,迁移完了懒得改回去。记住这应该是个临时方案。

2026 年正确入门的步骤

根据半年来的踩坑经验,按这个顺序走最稳:

第一步:ESLint 先上

不管用什么框架,先把 eslint-plugin-react-compiler 装上,规则设 error。跑一遍看看你的项目有多少违规,有多少组件符合条件。这一步不费什么时间,但能提前知道编译器能覆盖多少。

第二步:打开编译器,用 annotation 模式

别一上来就 all。官方设计了几档安全策略:

  • compilationMode: 'annotation':只处理标注了 "use memo" 的组件
  • compilationMode: 'infer':让编译器自己决定
  • compilationMode: 'all'(默认):全量处理

建议先从 annotation 开始,挑两三个关键组件测试,看有没有异常。

第三步:全量打开,跑一段时间

确认无误后切到 all。旧的 useMemouseCallback 不用急着删,编译器是增量的,你的手写缓存跟它不冲突。跑稳定了再慢慢清理。

第四步:清理老代码

把那些为了防性能问题而加的 memo 删掉,让代码回到最原始的形态。你写业务,编译器优化------这才是最终形态。

一句话总结

React Compiler 1.0 不是神器也不是噱头。它对新项目是白送的优化,对老项目是一次值得做的迁移。最大的价值不是帧数提升了多少,而是你写代码的时候少想了一件事。

至于要不要现在下手,如果项目是新的,直接上。如果是老项目,先跑 ESLint 看看情况再做决定。


更多阅读

相关推荐
JiaWen技术圈11 小时前
React 19 Fiber 架构 深度解析
前端·react.js·架构
暗冰ཏོ11 小时前
《Vue + React + Java + PHP 项目部署到服务器完整指南》
java·服务器·vue.js·react.js·项目部署
. . . . .11 小时前
React Native
react native·react.js
weixin_3975740911 小时前
ReAct推理链的工程化实现与最佳实践
前端·react.js·前端框架
Hejjon12 小时前
react-query 库使用案例
前端·javascript·react.js
JiaWen技术圈1 天前
Expo Router 和 React Native 的区别
javascript·react native·react.js
水云桐程序员1 天前
学习 React Native(简称 RN)的路径
学习·react native·react.js