likes
comments
collection
share

React Compiler现在可以使用啦!

作者站长头像
站长
· 阅读数 62

React Compiler 是一个新的实验性编译器。

它是一个仅构建时的工具,可以自动优化 React 应用程序。

它使用纯 JavaScript,并理解React 规则,因此无需重写任何代码即可使用它。

该编译器还包含一个eslint 插件,可以直接在编辑器中显示编译器的分析结果。

该插件独立于编译器运行,即使没有在应用程序中使用编译器也可以使用。

我们建议所有 React 开发人员使用此 eslint 插件来帮助提高代码库的质量。

编译器做什么?

编译器通过理解简单的 JavaScript 语义和React 规则来深入理解代码。这允许它向代码添加自动优化。

你可能熟悉通过useMemo、useCallback、 和进行手动记忆React.memo。如果代码遵循React 规则,编译器可以自动执行此操作。如果它检测到规则的破坏,它将自动跳过这些组件或挂钩,并继续安全地编译其他代码。

如果代码库已经被很好地记忆,可能不会期望看到编译器的重大性能改进。然而,在实践中,手动记住导致性能问题的正确依赖关系是很棘手的。

我应该尝试一下编译器吗?

请注意,该编译器仍处于实验阶段,并且有许多粗糙的地方。虽然它已在 Meta 等公司的生产中使用,但将编译器推出到应用程序的生产中将取决于代码库的健康状况以及遵循React 规则的程度。

现在不必急于使用编译器。可以等到它达到稳定版本后再采用它。

入门

将编译器部署到现有项目中

编译器旨在编译遵循React 规则的功能组件和钩子。它还可以通过退出(跳过)这些组件或挂钩来处理违反这些规则的代码。然而,由于 JavaScript 的灵活性,编译器无法捕获所有可能的违规行为,并且可能会出现漏报:也就是说,编译器可能会意外编译违反 React 规则的组件/钩子,从而导致未定义的行为。

因此,为了在现有项目中成功采用编译器,我们建议首先在产品代码中的一个小目录中运行它。可以通过将编译器配置为仅在一组特定目录上运行来实现此目的:

const ReactCompilerConfig = {
  sources: (filename) => {
    return filename.indexOf('src/path/to/dir') !== -1;
  },
};

在极少数情况下,还可以使用 compilationMode: "annotation" 选项将编译器配置为在“选择加入”模式下运行。

这使得编译器只会编译用指令注释的组件和钩子"use memo"。请注意,该annotation模式是一种临时模式,旨在帮助早期采用者,我们不打算"use memo"长期使用该指令。

const ReactCompilerConfig = {
  compilationMode: "annotation",
};

// src/app.jsx
export default function App() {
  "use memo";
  // ...
}

也可以将覆盖范围扩大到其他目录,并慢慢将其推广到整个应用程序。

将编译器部署到新项目中

如果要开始一个新项目,可以在整个代码库上启用编译器,这是默认行为。

安装

检查兼容性

在安装编译器之前,可以先检查代码库是否兼容:

npx react-compiler-healthcheck

该脚本将:

  • 检查可以成功优化的组件数量:数量越高越好
  • 检查使用情况:启用并遵循此功能意味着遵循React 规则的可能性更高
  • 检查不兼容的库使用情况:已知与编译器不兼容的库

举个例子:

Successfully compiled 8 out of 9 components.
StrictMode usage not found. 
Found no usage of incompatible libraries.

安装 eslint-plugin-react-compiler

React Compiler 还支持 eslint 插件。 eslint 插件可以独立于编译器使用,这意味着即使不使用编译器也可以使用 eslint 插件。

npm install eslint-plugin-react-compiler

然后,将其添加到eslint 配置中:

module.exports = {
  plugins: [
    'eslint-plugin-react-compiler',
  ],
  rules: {
    'react-compiler/react-compiler': "error",
  },
}

与 Babel 一起使用

npm install babel-plugin-react-compiler

编译器包含一个 Babel 插件,可以在构建管道中使用该插件来运行编译器。

安装后,将其添加到 Babel 配置中。请注意,编译器首先在管道中运行至关重要:

// babel.config.js
const ReactCompilerConfig = { /* ... */ };

module.exports = function () {
  return {
    plugins: [
      ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first!
      // ...
    ],
  };
};

babel-plugin-react-compiler应先于其他 Babel 插件运行,因为编译器需要输入源信息进行声音分析。

与Vite一起使用

如果你使用Vite,可以将插件添加到vite-plugin-react中:

// vite.config.js
const ReactCompilerConfig = { /* ... */ };

export default defineConfig(() => {
  return {
    plugins: [
      react({
        babel: {
          plugins: [
            ["babel-plugin-react-compiler", ReactCompilerConfig],
          ],
        },
      }),
    ],
  };
});

与 Next.js 一起使用

Next.js 有一个实验性配置来启用 React 编译器。它会自动确保 Babel 设置为babel-plugin-react-compiler.

  • 安装 Next.js canary,它使用 React 19 Release Candidate
  • 安装babel-plugin-react-compiler
npm install next@canary babel-plugin-react-compiler

然后配置实验选项next.config.js:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};

module.exports = nextConfig;

使用实验选项可确保在以下方面支持 React 编译器:

  • App Router
  • Pages Router
  • Webpack (default)
  • Turbopack

与 Webpack 一起使用

可以为 React Compiler 创建自己的 Loader,如下所示:

const ReactCompilerConfig = { /* ... */ };
const BabelPluginReactCompiler = require('babel-plugin-react-compiler');

function reactCompilerLoader(sourceCode, sourceMap) {
  // ...
  const result = transformSync(sourceCode, {
    // ...
    plugins: [
      [BabelPluginReactCompiler, ReactCompilerConfig],
    ],
    // ...
  });

  if (result === null) {
    this.callback(
      Error(
        `Failed to transform "${options.filename}"`
      )
    );
    return;
  }
  this.callback(
    null,
    result.code
    result.map === null ? undefined : result.map
  );
}

module.exports = reactCompilerLoader;

注意

使用 React Compiler 时可能会出现几大类错误:

  • 由于违反React 规则而导致的运行时错误或不正确的行为无法被静态捕获
  • 运行时无限循环和/或 effect 过度触发
  • 构建管道中的构建时错误

编译器可以静态检测大多数编译组件或挂钩不安全的情况。

例如,编译器假设代码遵循React 规则,因此当它检测到规则已被破坏时,它将安全地跳过违规组件和挂钩,并继续编译其他安全文件。

由于 React Compiler 当前的优化应用于函数级别(这意味着编译器仅查看组件或钩子主体内部的代码),因此可以安全地有选择地优化某些组件和钩子并跳过其他组件和钩子。

然而,由于 JavaScript 的灵活和动态特性,不可能全面检测所有情况。在这些情况下可能会出现错误和未定义的行为(例如无限循环)。

常见问题

(0 , _c) is not a function错误?

当不使用 React 19 Beta 及更高版本时,在 JavaScript 模块评估期间会发生这种情况。要解决此问题,请先将应用程序升级到 React 19 Beta。

调试

React Devtools (v5.0+) 内置了对 React Compiler 的支持,并将在已由编译器优化的组件旁边显示“Memo ✨”徽章。

编译后出现错误或无法运行

如果存在运行时问题,请首先尝试通过积极选择认为可能与指令相关的任何组件或挂钩来消除错误或错误"use no memo":

function SuspiciousComponent() {
  "use no memo"; // opts out this component from being compiled by React Compiler
  // ...
}

"use no memo"是一个临时逃生口,可选择退出 React 编译器编译的组件和钩子。

应用程序反复挂起或触发效果

如果应用程序遇到错误、无限循环或 Effect 触发过多,则可能发生的情况是 React Compiler 没有以与之前完全相同的方式记忆对 hooks 的依赖关系。

我们将此称为“正确性”问题,因为应用程序使用记忆的依赖项作为钩子的输入以实现正确的行为。

默认情况下,编译器将尝试保留手动记忆,并在组件不匹配时跳过优化,因此实际上不应发生这种情况。

转载自:https://juejin.cn/post/7371013983367807010
评论
请登录