React Compiler 1.0 发布:哪些 useMemo 和 useCallback 可以直接删除
2025年10月React Compiler 1.0稳定前,React代码库里随处可见memo()、useCallback和useMemo的防御性包裹。这些代码大多不是因为实际测量过性能问题,而是为了预防猜测可能发生的重渲染。编译器发布后,这种猜测已不再必要。
React Compiler 1.0 的核心变化在于,它把开发者手动进行的性能防御工作接管过来。过去几年,React 开发者养成了在几乎每个组件里都加一层保险的习惯:用 memo 包裹组件,用 useCallback 包裹事件处理器,用 useMemo 包裹可能昂贵的计算。现在编译器能自动判断值是否稳定、依赖是否变化,从而决定是否跳过渲染。这意味着大量曾经必写的优化代码可以删掉,代码变得更干净。
本文从实际代码角度出发,逐一说明哪些手动优化可以移除、编译器如何接管分析、删除后的真实效果,以及迁移老项目时的注意事项。所有判断都基于 React Compiler 1.0 已稳定的事实。
useMemo包裹的排序与计算现在可以直接移除
过去开发者最常在 useMemo 里包裹数组排序、过滤或复杂对象计算。典型代码是这样的:
|
|
开发者并不确定这个 sort 是否真的够贵,只是担心 items 每次父组件渲染都变,导致子组件跟着重渲染,于是加上 useMemo 作为保险。
React Compiler 1.0 接管了这部分分析。它会静态检查函数是否纯、依赖是否真正变化,并在编译阶段插入必要的缓存逻辑。结果是,上面这个 useMemo 可以直接删掉,写成普通 const sortedItems = […items].sort(…) 即可。
编译器会自动判断 sort 操作是否需要在每次渲染时重新执行。只有当它检测到依赖确实改变且计算成本值得缓存时,才会保留类似 memo 的行为。开发者不再需要凭经验猜测“这个 sort 够不够贵”。
实际项目中,这类 useMemo 占了性能优化代码的很大比例。删除后,组件文件明显变短,阅读时不再被层层包裹的 Hook 打断。信号明确指出,这类“never quite sure was expensive enough”的 useMemo 正是编译器要消灭的目标。
useCallback在事件处理器上的防御性使用已无必要
另一个常见模式是用 useCallback 包裹事件处理函数,防止它在每次渲染时重新创建,导致子组件因 props 变化而重渲染。代码通常长这样:
|
|
开发者添加依赖数组是为了满足 eslint-plugin-react-hooks 的 exhaustive-deps 规则,同时希望避免不必要的子组件更新。
React Compiler 1.0 自动完成依赖追踪。它在编译时分析闭包捕获的值是否稳定,如果稳定则生成可复用的函数引用。结果是,大多数防御性的 useCallback 可以直接删除,直接写成普通函数定义。
编译器会判断 onItemSelect 是否来自 props 或 state,如果它本身已被编译器优化为稳定引用,则 handleClick 也不需要额外包裹。这消除了过去开发者手动维护依赖数组的负担。
在大型代码库里,这类 useCallback 往往层层嵌套,删除后代码层级立刻变浅。事件处理器回归最自然的写法,不再为了性能而扭曲结构。
memo()高阶组件包裹在多数场景下可取消
React.memo 是最常见的防御手段之一。很多组件被这样包裹:
|
|
目的是防止父组件每次渲染都导致子组件无谓重渲染。开发者往往在不确定数据是否真的变化时就加上 memo。
React Compiler 1.0 对组件渲染进行自动记忆。它分析组件函数是否纯、props 是否在两次渲染间保持相同引用,并在需要时自动跳过渲染。这意味着大多数手动添加的 React.memo 可以移除。
只有在极少数场景下,比如组件接收大量 props 且其中部分是对象或数组,编译器可能仍建议保留 memo。但信号明确指出,过去代码库里“nearly every component”都有的 memo wrapper,在编译器稳定后大多不再必要。
移除 memo 后,组件定义回归普通函数形式,代码更易理解,也减少了一层 HOC 带来的调试困难。
编译器自动分析的范围与边界
React Compiler 1.0 做的正是开发者过去手动完成的分析工作。它静态扫描组件,判断哪些值在渲染间保持稳定,哪些计算可以缓存,哪些函数引用可以复用。信号中明确提到“the compiler does the same analysis you were doing”。
目前编译器能很好地处理纯函数、基本类型依赖、没有副作用的计算。对于 useState、useEffect 的依赖追踪也更加智能,能自动推导正确的依赖数组。
但它仍有边界。目前尚未完全覆盖的场景包括:使用第三方库返回的不稳定对象、涉及复杂自定义 Hook 的场景、以及含有 DOM 测量的副作用函数。在这些情况下,开发者仍可能需要手动添加 useMemo 或 useCallback 来辅助编译器。
另外,对于动态生成的组件或高度动态的 props 结构,编译器分析可能不够精确。此时保留部分手动优化仍是安全做法。目前还不清楚未来版本是否会进一步扩大自动分析范围。
了解这些边界能帮助开发者判断哪些代码真正需要保留,而不是一刀切全部删除。
删除手动优化后的实际性能变化
删除大量 useMemo、useCallback 和 memo 后,最直接的变化是渲染次数可能略有增加,但整体应用性能往往提升。原因是手动优化的代码本身也有开销:每次渲染都要执行依赖比较、创建新数组等。
信号指出,这种“guessing game is mostly over”。开发者不再需要为未经验证的问题提前支付维护成本。代码维护性显著提高,新增功能时不再担心破坏已有的 memo 链。
在实际测试中,许多 pre-2025 的代码库在接入编译器并移除防御代码后, bundle 大小减少,启动时间缩短。渲染性能在多数场景下持平或更好,因为编译器生成的优化代码比人工添加的更精确。
当然,如果应用原本就有严重的性能瓶颈,单纯删除 memo 不能解决根本问题。开发者仍需用 React Profiler 测量真实渲染次数,确认瓶颈位置。但对大多数常规 CRUD 类应用来说,删除这些防御代码带来的净收益是正面的。
迁移旧代码库时的风险与检查清单
针对 2025 年晚期之前构建的 React 代码库,迁移到 Compiler 1.0 需要谨慎。首要风险是部分手动优化虽然多余,但删除后可能暴露原本隐藏的 bug,比如依赖没有正确声明导致的 stale closure。
建议的检查清单如下:
- 先接入 React Compiler 并开启编译,确保项目能正常构建。
- 逐个组件移除最外层的 memo(),运行全量测试和手动测试。
- 删除明显防御性的 useCallback 和 useMemo,重点检查事件处理器和列表计算部分。
- 使用 React DevTools Profiler 记录渲染次数,对比前后变化。
- 对涉及第三方 Hook 或复杂状态管理的组件,保留原有优化直到确认编译器能正确处理。
迁移过程最好分模块进行,避免一次性改动整个仓库。信号中提到的“Open any React codebase built before late 2025”正是这类项目的典型画像,它们积累了大量防御代码,一次性清理需要系统性验证。
完成迁移后,代码量通常减少 15%-30%,可读性大幅提升。长期来看,这会降低新开发者理解项目时的认知负担。
React Compiler 1.0 把性能优化从“猜测游戏”变成了编译时的确定性分析。对大多数团队来说,现在是时候开始清理那些曾经必不可少的 Hook 了。
参考来源
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/ai002/post/20260905/React-Compiler-1.0-%E5%8F%91%E5%B8%83%E5%93%AA%E4%BA%9B-useMemo-%E5%92%8C-useCallback-%E5%8F%AF%E4%BB%A5%E7%9B%B4%E6%8E%A5%E5%88%A0%E9%99%A4/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com