React Compiler Plugin

126 阅读5分钟

一、简介

React 编译器会在构建时自动优化你的 React 应用

【困扰:React 的性能也已经足够快,但有时你需要手动对组件和值进行记忆化(memoization)以保持应用的响应速度。这种手动记忆化既繁琐又容易出错,并且会增加需要维护的额外代码】

  • 使用之前

image.png

手动记忆化存在一个会破坏记忆化效果的细微 bug:

<Item key={item.id} onClick={() => handleClick(item)} />

尽管 handleClick 被 useCallback 包裹,但每次组件渲染时,箭头函数 () => handleClick(item) 都会创建一个新的函数。这意味着 Item 总会接收到一个新的 onClick prop,从而破坏了记忆化效果

  • 使用之后

使用 React 编译器,你可以编写相同的代码而无需手动进行记忆化:

image.png

二、安装

【前提条件】

React 编译器专为与 React 19 配合使用而设计,但也支持 React 17 和 18。在插件中通过target属性指定react版本进行对应的版本编译。(注意:17和18需要安装 react-compiler-runtime 包)

React 19 使用内置的 react/compiler-runtime,React 17/18 使用独立包 react-compiler-runtime

{
    target:  '19' // 或 '18' 和 '17'
}

【安装命令】

pnpm install -D babel-plugin-react-compiler @latest

注意:React 编译器必须在你的 Babel 插件管道中 首先 运行。编译器需要原始的源代码信息来进行正确的分析,因此它必须在其他转换操作之前处理你的代码

module.exports = {
    plugins: [
        'babel-plugin-react-compiler' ,  // 必须首先运行!
        // ... 其他插件
    ],
    // ... 其他配置
};

【ESLint集成支持】

React 编译器包含一条 ESLint 规则,可帮助识别无法优化的代码。当 ESLint 规则报告错误时,意味着编译器将跳过对该特定组件或 Hook 的优化。这是安全的:编译器将继续优化代码库的其他部分。你不需要立即修复所有违规之处。可以按照自己的节奏逐步解决这些问题,以逐渐增加已优化组件的数量。

安装

`npm install -D eslint-plugin-react-hooks``@latest`

参考文档:github.com/facebook/re…

【验证是否经过React编译器优化】

1、检查React DevTools

React 编译器优化的组件会在 React DevTools 中显示一个 “Memo ✨” 徽章

2、检查构建输出

编译后的代码将包含编译器自动添加的自动记忆化逻辑

【故障排查】

如果某个组件在编译后引发问题,可以使用 "use no memo" 指令暂时将其排除

function ProblematicComponent() {
    "use no memo";
    // 这里是组件代码
}

三、插件配置项解析

1、编译控制 - compilationMode 【控制选择要编译的函数的策略】

{
    compilationMode:  'infer' // 或 'annotation''syntax''all'
}
  • 'infer' (默认值):编译器使用智能的启发式方法来识别 React 组件和 Hook:

    • 明确使用 "use memo" 指令注释的函数
    • 命名类似组件(PascalCase)或 Hook(use 前缀)并且创建了 JSX 和/或调用了其他 Hook 的函数
  • 'annotation' :仅编译使用 "use memo" 指示符明确标记的函数。是增量采用的理想选择。

  • 'syntax' :仅编译使用 Flow 的 component 和 hook 语法的组件和 Hook。【无法与ts一起使用】

  • 'all' :编译所有顶层函数。不推荐,因为它可能会编译非 React 函数。

  • 注意:无论在哪种模式下,带有 "use no memo" 指令的函数总会被跳过

image.png

2、版本兼容性 - target【确保编译器生成的代码与你的 React 版本兼容】

image.png

3、错误处理 - panicThreshold【控制编译器如何处理不遵循 React 规则 的代码,决定是让构建失败还是跳过存在问题的组件】

image.png

  • 'none'  (默认, 推荐): 跳过无法编译的组件并继续构建
  • 'critical_errors' : 仅在关键编译器错误时使构建失败
  • 'all_errors' : 遇到任何编译诊断即使构建失败

4、调试 - logger【为编译事件提供自定义日志功能】

image.png

方法 

  • logEvent:传入文件名和事件详情来记录每次编译器事件

事件类型 

  • CompileSuccess:函数成功编译
  • CompileError: 由于错误而跳过该函数
  • CompileDiagnostic:非致命的诊断信息
  • CompileSkip: 因其他原因跳过该函数
  • PipelineError: 意外的编译管线错误
  • Timing:性能计时信息

获取详细错误日志信息

image.png

5、Feature Flags - gating【用运行时的特性开关,用于 A/B 测试或渐进式发布】

gating 选项启用条件编译,允许你控制是否在运行时使用优化代码

image.png

  • source:用于导入特性开关的模块路径
  • importSpecifierName:要导入的已导出函数的名字

注意事项 

  • gating 函数必须返回布尔值
  • 同时包含编译版本与原始版本会增加包大小
  • 所有包含已编译函数的文件都会被添加该导入

用法

1、创建一个特性开关模块:

image.png

2、配置编译器:

image.png

3、编译器将生成 gated 代码:

image.png

注意:gating 函数在模块加载时只会执行一次,因此一旦 JS 包被解析并执行,组件的选择将在本次浏览器会话的剩余时间内保持不变

四、指令

React 编译器指令是特殊的字符串文字,用于控制特定函数是否被编译

image.png

可用的指令 

  • "use memo"  - 让一个函数选择加入编译[强制编译]:当使用 annotation 模式时,或需要覆盖 infer 模式的推断逻辑时
  • "use no memo"  - 让一个函数选择退出编译[阻止编译]:调试问题或处理不兼容的代码时

1、函数级别

image.png

2、模块级别

image.png