React Compiler 可以逐步采用,允许你首先在代码库的特定部分尝试使用。本指南将向你展示如何在现有项目中逐步推广该编译器的使用。
你将会学习到
- 为何推荐增量式采用
- 使用 Babel 覆写进行基于目录的采用
- 使用 “use memo” 指令进行选择性编译
- 使用 “use no memo” 指令排除组件
- 带有 gating 的运行时特性标志
- 监控你的采用进度
为何采用渐进式迁移?
React Compiler 的设计目的是自动优化您的整个代码库,但您不必一次性全部采用。渐进式采用让您能够控制推行过程,在扩展到其余部分之前,先在应用程序的小部分上测试编译器。
从小处着手有助于建立对编译器优化的信心。你可以验证应用在编译代码下的行为是否正确,测量性能提升,并识别代码库中的任何特定边缘情况。这种方法对于稳定性至关重要的生产应用程序尤其有价值。
渐进式采用还使得更容易处理编译器可能发现的任何 React 规则违规。你无需一次性修复整个代码库中的违规问题,而可以在扩展编译器覆盖范围的同时有条不紊地解决这些问题。这使迁移过程更易于管理,并降低了引入错误的风险。
通过控制代码中哪些部分被编译,你还可以运行 A/B 测试以衡量编译器优化在实际应用中的效果。这些数据有助于你做出是否全面采用的明智决策,并向团队展示其价值。
渐进式采用的方法
有三种主要方法可以逐步采用 React 编译器:
- 覆盖 Babel - 将编译器应用于特定目录
- 通过 “use memo” 选择加入 - 仅编译明确选择加入的组件
- 运行时控制 - 通过功能标志控制编译
所有方法都允许你在完全上线之前,在应用程序的特定部分上测试该编译器。
基于目录的采用与 Babel 覆盖
Babel 的 overrides
选项允许你将不同的插件应用于代码库的不同部分。这对于逐步在各个目录中采用 React 编译器非常理想。
基本配置
首先将编译器应用到特定目录:
// babel.config.js
module.exports = {
plugins: [
// Global plugins that apply to all files
],
overrides: [
{
test: './src/modern/**/*.{js,jsx,ts,tsx}',
plugins: [
'babel-plugin-react-compiler'
]
}
]
};
扩展覆盖范围
随着你信心的增加,添加更多目录:
// babel.config.js
module.exports = {
plugins: [
// Global plugins
],
overrides: [
{
test: ['./src/modern/**/*.{js,jsx,ts,tsx}', './src/features/**/*.{js,jsx,ts,tsx}'],
plugins: [
'babel-plugin-react-compiler'
]
},
{
test: './src/legacy/**/*.{js,jsx,ts,tsx}',
plugins: [
// Different plugins for legacy code
]
}
]
};
按重写配置编译器选项
还可以按重写配置编译器选项:
// babel.config.js
module.exports = {
plugins: [],
overrides: [
{
test: './src/experimental/**/*.{js,jsx,ts,tsx}',
plugins: [
['babel-plugin-react-compiler', {
// options ...
}]
]
},
{
test: './src/production/**/*.{js,jsx,ts,tsx}',
plugins: [
['babel-plugin-react-compiler', {
// options ...
}]
]
}
]
};
选择加入模式
如需最大程度的控制,您可以使用 compilationMode: 'annotation'
,仅编译那些通过 "use memo"
指令显式选择加入的组件和 Hook。
选择加入模式配置
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
compilationMode: 'annotation',
}],
],
};
选择加入指令
在要编译的函数开头添加 "use memo"
:
function TodoList({ todos }) {
"use memo"; // Opt this component into compilation
const sortedTodos = todos.slice().sort();
return (
<ul>
{sortedTodos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
function useSortedData(data) {
"use memo"; // Opt this hook into compilation
return data.slice().sort();
}
使用 compilationMode: 'annotation'
时,你必须:
- 在每个需要优化的组件中添加
"use memo"
- 在每个自定义 Hook 中添加
"use memo"
- 记得在新组件中也添加它
这可以在你评估编译器影响的同时,精确控制哪些组件会被编译。
运行时门控
gating
选项使您能够在运行时使用功能标志控制编译。这对于运行A/B测试或根据用户细分逐步推出编译器非常有用。
运行时门控工作原理
编译器会在优化后的代码周围添加运行时检查。如果开关返回 true
,则运行优化版本;否则运行原始代码。
门控配置
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
gating: {
source: 'ReactCompilerFeatureFlags',
importSpecifierName: 'isCompilerEnabled',
},
}],
],
};
实现功能标志
创建一个模块来导出你的门控函数:
// ReactCompilerFeatureFlags.js
export function isCompilerEnabled() {
// Use your feature flag system
return getFeatureFlag('react-compiler-enabled');
}
故障排除
如果在采用过程中遇到问题:
- 使用
"use no memo"
临时排除有问题的组件 - 查阅 调试指南 以了解常见问题
- 修复 ESLint 插件识别出的 React 规则违规
- 考虑使用
compilationMode: 'annotation'
以更渐进的方式采用