React Compiler 正式落地一年:告别手动 useMemo/useCallback 的全栈实践

React Compiler 正式落地一年:告别手动 useMemo/useCallback 的全栈实践 React Compiler 正式落地一年告别手动 useMemo/useCallback 的全栈实践![封面](https://picsum.photos/seed/178573765850/800/400)2025 年 10 月 7 日React 官方发布 React Compiler 1.0一年后的今天它已经成为 Next.js 16、Vite、Expo 的默认配置。曾经靠 useMemo、useCallback、React.memo 手动优化的时代正在被编译器自动记忆化全面取代。一、为什么我们需要它手动 memo 的痛点写过中大型 React 应用的开发者都懂这种痛为了减少不必要的重渲染你要在代码里到处埋 useMemo、useCallback、React.memo。稍不留神依赖数组写错还会引入诡异难查的 bug——缓存了不该缓存的值或者该缓存的反而不缓存。看一个典型的列表组件import { useMemo, useCallback, memo } from react; const Item memo(function Item({ item, onToggle }: { item: { id: number; text: string; done: boolean }; onToggle: (id: number) void; }) { return ( li onClick{() onToggle(item.id)} style{{ textDecoration: item.done ? line-through : none }} {item.text} /li ); }); export function TodoList({ todos }: { todos: { id: number; text: string; done: boolean }[] }) { const visibleTodos useMemo( () todos.filter((t) !t.done), [todos] ); const handleToggle useCallback((id: number) { console.log(toggle, id); }, []); return ul{visibleTodos.map((item) Item key{item.id} item{item} onToggle{handleToggle} /)}/ul; }这段代码的每个 memo 都是人肉编译器在工作判断依赖、决定缓存策略。这恰恰是编译器最擅长的事——把规则写死、分析数据流、在构建期自动完成。而且手动 memo 还有一个致命问题它的正确性完全依赖开发者的自觉你漏写一个性能就悄悄劣化你写错一个依赖bug 就悄悄出现。二、React Compiler 是什么React Compiler 是一个自动记忆化优化编译器。它不改变 React 的编程模型而是把你的组件代码编译成已经手动优化好的样子——自动对组件和 Hook 中的值进行细粒度记忆化包括 JSX 本身。它的工作原理与传统 Babel 插件不同编译器先把 Babel 提供的 AST 降级成自己设计的HIRHigh-level Intermediate Representation高层中间表示再通过多轮编译 pass 仔细分析数据流data-flow和代码可变性mutability判断哪些值在渲染中真正依赖、哪些可以安全缓存。简单说你在运行时靠 hook 做的事它搬到了构建时靠静态分析完成。源代码 → Babel AST → HIR → 多轮分析 pass数据流/可变性→ 自动插入记忆化 → 优化后的 JS官方在 1.0 博客中明确建议升级 eslint-plugin-react-hooks 到最新版因为编译器相关的 ESLint 规则已经合并进了该插件的 recommended 预设原独立的 eslint-plugin-react-compiler 已弃用。lint 规则不需要安装编译器本体就能运行零风险建议所有人今天就升级。三、快速接入Next.js 16 与 Vite 82026 年的新项目几乎不用额外配置。Next.js 16 默认开启 React CompilerVite 生态则需要留意一个关键变化vitejs/plugin-react v6 抛弃了 Babel改用 oxcRust 实现做 JSX 转换和 Fast Refresh因此老写法 react({ babel: { plugins: [...] } }) 不再生效Babel 系的插件需要借助 rolldown/plugin-babel 接入。npm i -D vitejs/plugin-react rolldown/plugin-babel babel-plugin-react-compiler// vite.config.ts —— 2026 年 Vite 8 的正确姿势 import react from vitejs/plugin-react; import babel from rolldown/plugin-babel; export default defineConfig({ plugins: [ babel({ babelPlugins: [babel-plugin-react-compiler], }), react(), ], });ESLint 侧更简单规则已在 eslint-plugin-react-hooks v7 的 recommended 预设里// eslint.config.js (Flat Config) import reactHooks from eslint-plugin-react-hooks; import { defineConfig } from eslint/config; export default defineConfig([ reactHooks.configs.flat.recommended, ]);配置完跑一遍 npx react-compiler-healthcheck它会扫描你的代码库告诉你哪些文件存在编译器无法处理的写法以及潜在的错误模式。四、实战对比编译器做了什么接上文的 TodoList开启 React Compiler 后你只需要写最朴素、最直白的版本剩下的交给编译器export function TodoList({ todos }: { todos: Todo[] }) { const visibleTodos todos.filter((t) !t.done); const handleToggle (id: number) { console.log(toggle, id); }; return ( ul {visibleTodos.map((item) ( li key{item.id} onClick{() handleToggle(item.id)} style{{ textDecoration: item.done ? line-through : none }} {item.text} /li ))} /ul ); }注意这里没有 useMemo、没有 useCallback、没有 memo——编译器会在构建期自动1. 对 visibleTodos 做记忆化仅在 todos 变化时重算2. 对 handleToggle 做记忆化保证引用稳定3. 对 JSX 做记忆化让 todos 未变时整棵子树直接复用4. 对列表项 自动做类似 memo 的粒化处理。这意味着性能优化的正确性从开发者自觉变成了编译器的确定性行为。团队里新来的同学哪怕完全不懂 memo写出来的代码性能也不会差。五、注意事项与逃生舱编译器不是魔法它要求你的代码遵守Rules of React不修改 props/state、组件保持纯函数、不滥用 ref 等。为此官方把 eslint-plugin-react-hooks 的规则从 warn 提升到 error 是不少团队的迁移第一步。如果你的某处代码确实需要绕过编译器比如某个高频更新的第三方组件内部有特殊模式可以用指令级逃生舱use no memo; // 文件顶部整个文件关闭自动记忆化 function ThirdPartyHeavyComponent() { // 这里保持手写 useMemo/useCallback 的旧模式 }迁移策略上社区的主流共识是新项目直接开启存量项目先把 lint 规则提到 error 修完违规代码再渐进开启实在改不动的老代码用 use no memo 兜底。React 团队在 1.0 发布时也强调编译器会持续优化即使你的代码已经手动 memo 过开启后依然可能获得额外收益——因为编译器能看到人眼容易忽略的粒化机会。六、总结回看 2026 年的前端技术版图服务端组件RSC解决了数据获取的边界问题React Compiler 解决了性能优化的心智负担问题两者合力把 React 全栈开发推向写直觉代码拿最优性能的新常态。正如社区里那句调侃2026 年再看到 useMemo就像看到手写 for 循环实现数组 map——能用但你已经有了更好的工具。前端开发的演进从来不是消灭某种写法而是把某种写法从必修课降级为冷知识。React Compiler 做到的正是这件事让性能优化回归编译器让开发者回归业务。如果你还没升级今天就是最好的时机——先升级 eslint-plugin-react-hooks 跑一遍 lint零成本收益立现。