React Compiler 1.0 来了:以后useMemo 和 useCallback 可以删了?

by JeariCk 2 min read
react logo

2025 年 10 月,React 官方正式发布了 React Compiler 1.0 稳定版。如果你两三年没碰 React 生态,可能觉得:”哦,又一个新工具。”但这次不一样——这东西大概率会改变你接下来几年的写代码方式。

react logo
react logo

这东西到底是什么?

简单说,React Compiler 是一个编译时优化工具。你跑构建的时候它跑一遍你的代码,自动分析哪些计算可以缓存、哪些函数该记下来、哪些组件不用重新渲染——然后帮你把优化逻辑塞进去。你什么都不用写,它自己干。

这个项目内部代号叫 React Forget,意思就是”忘掉手动优化这回事吧”。React 团队从 2022 年就开始搞,花了三年多才推到稳定版。

核心变化一句话:之前你手动写的 `useMemo`、`useCallback`、`React.memo`,大部分都不需要了。

手动优化的日子,真的受够了

写过两年以上 React 的话,下面这些场景你肯定不陌生。

项目刚开的时候,代码清清爽爽:

“`jsx

function ProductList({ products, filter }) {
 const filtered = products.filter(p => p.category === filter);
 const sorted = filtered.sort((a, b) => a.price - b.price);
 const total = sorted.reduce((sum, p) => sum + p.price, 0);

return (
<div>
<p>总计: {total}</p>
<p>{sorted.map(p => <productcard key="{p.id}" product="{p}"></productcard>)}</p>
</div>
)
}


“`

跑着跑着发现性能不对,开始一个个往上堆 memo:

“`jsx

function ProductList({ products, filter }) {
 const filtered = useMemo(
 () => products.filter(p => p.category === filter),
 [products, filter]
 );
 const sorted = useMemo(
 () => [...filtered].sort((a, b) => a.price - b.price),
 [filtered]
 );
 const total = useMemo(
 () => sorted.reduce((sum, p) => sum + p.price, 0),
 [sorted]
 );

return (
  <div>
  <p>总计: {total}</p>
  <p>{sorted.map(p => <productcard key="{p.id}" product="{p}"></productcard>)}
</p></div>
)
}


“`

看着就烦对吧?更烦的是依赖数组写错了查半天,还有那种祖传代码里到处是 useContextSelector 和几十个 memo 的,改起来跟拆地雷一样,炸了都不知道该怪谁。

React 团队自己也明白这个。Lauren Tan(React Compiler 负责人)发布时说了一段挺坦诚的话:”我们花了好多年教开发者怎么用 useMemo,现在又要告诉你们不用写了——确实有点讽刺。但我们觉得这才是正确的方向。”

它是怎么工作的?

React Compiler 不在运行时干活,而是在编译阶段对你的代码做静态分析。它用了不少编译器领域的成熟技术——控制流分析、数据流分析,还有自定义的 SSA(Static Single Assignment)IR 表示。

你写:

“`jsx

function UserProfile({ user }) {
 const displayName = `${user.firstName}  ${user.lastName}`.toUpperCase();
 return <h1 class="wp-block-heading">{displayName}</h1>;
}



“`

编译器跑一遍能发现 `displayName` 只有 `user` 变了才需要重新算,于是自动帮你加了缓存。你完全看不到这些,源码还是干干净净。

React Compiler 遵循一套叫 “Rules of React” 的约束系统。只要你的代码符合规则——比如不直接改 state、不改变 hooks 调用顺序——编译器就能安全地帮你优化。eslint-plugin-react-hooks 的规则集已经跟编译器打通了,跑一遍 ESLint 就能提前知道哪些代码编译器处理不了。

如果你确实需要手动控制,可以在文件顶部加一行 `”use no memo”;`,编译器就会跳过这个文件。这是逃生阀,但绝大多数情况用不上。

next.js - react应用开发框架
next.js – react应用开发框架

各框架的集成情况

发布之后的生态跟进速度比我预想的快。

Next.js 从 15.3.1 开始稳定支持。Next.js 16 更是默认开箱即用——新建项目直接启用,零配置。

Expo SDK 54 及以上版本默认开启,新的 RN 项目一开就能用。这对 React Native 开发者来说是个好消息——移动端性能优化一直是老大难,大多数 RN 项目的 memo 覆盖率低得可怜。

Vite 这边有个坑。`@vitejs/plugin-react` v6 配合 Vite 8 把内置的 Babel 换成了 oxc,所以老的 `react({ babel: { plugins: […] } })` 写法跑不通了。正确做法是用 `@vitejs/plugin-react` 导出的 `reactCompilerPreset()` 方法。

还有个正在做的 swc 插件,目标是进一步提升构建性能。如果你特别在意构建速度,可以留意一下。

好消息和一些坑

好消息

– 新项目零配置享受自动优化
– ESLint 提前帮你排查编译器可能搞不定的代码
– Next.js、Expo、Vite 都第一时间跟进了
– 小项目编译后二进制体积基本没变化

需要注意的

老项目别急着删 useState 和 useCallback。直接删了可能跑出无限循环——编译器的逻辑可能跟你之前的手动缓存策略打架。安全做法是先启用编译器验证它能正常处理,运行稳定了再慢慢清理。
effect 依赖还是要自己管。编译器不会帮你管 `useEffect` 的依赖,因为 effect 触发时机的精确控制和渲染优化是两码事。
代码必须符合 Rules of React。不遵守的话——比如直接改 props、hooks 不按顺序——编译器就安静地跳过,不会报错。所以建议先让 eslint-plugin-react-hooks 跑到零告警再开。

所以

React Compiler 1.0 不是实验品了,是可以上生产的东西。一句话概括:从”按约定优化”变成了”按编译优化”

下一个你新建的 React 项目,大概率已经自带这能力了。你只管写干净的代码,编译器把脏活干了。至于老项目里的 useMemo 和 useCallback——先留着,别急着动。等编译器跑稳了再慢慢清也不迟。

毕竟好代码的标准从来不是”看起来优化了”,而是”真的不卡 + 你能看懂”。

📖 推荐阅读

这些也是跟前端工程师有关的文章,你可能会感兴趣:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注