React 编译器

article/2025/8/26 12:19:36

在这里插入图片描述

🤖 作者简介:水煮白菜王,一位前端劝退师 👻
👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。
感谢支持💕💕💕
本文内容参考自 React文档 - React Compiler 内容进行记录和整理✍

目录

  • React 编译器
    • 编译器是做什么的?
      • 注意
      • React Compiler 添加了什么样的记忆?
        • 优化重新渲染
        • 昂贵的计算也会被记忆化
    • 开始使用
      • 安装 eslint-plugin-react-hooks
      • 将编译器推出到您的代码库
      • 现有项目
      • 新项目
      • 在 React 17 或 18 中使用 React 编译器
      • 在库上使用编译器
    • 用法
      • Babel
      • Vite
      • Next.js :
      • Remix
      • Webpack
    • 故障 排除
      • 编译器假定什么?
      • 如何知道我的组件已优化?
      • 编译后某些内容无法正常工作
  • 如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀

React 编译器

本文将为你介绍 React Compiler 以及如何成功试用它

本文您进入React 编译器将学习
编译器入门
安装编译器和 ESLint 插件
进行故障排除

注意
React Compiler 是目前在 RC 中的一个新编译器,React已经开源了它以从社区获得反馈。现在建议大家试用编译器并提供反馈。
最新的 RC 版本可以通过标签找到,每日实验版本可以使用 .@rc@experimental

React Compiler 是React开源的一个新的编译器,用于从社区获取反馈。它是一个仅限构建时的工具,可自动优化您的 React 应用程序。它与纯 JavaScript 一起工作,并且理解 React 的规则,因此你不需要重写任何代码来使用它。

eslint-plugin-react-hooks 还包括一个 ESLint 规则,该规则直接在编辑器中显示来自编译器的分析。React强烈建议大家立即使用 Linter。Linter 不需要您安装编译器,因此即使您还没有准备好试用编译器,也可以使用它。

该编译器目前作为 发布,可以在 React 17+ 应用程序和库上试用。要安装 RC:rc
npm

npm install -D babel-plugin-react-compiler@rc eslint-plugin-react-hooks@^6.0.0-rc.1

yarn

yarn add -D babel-plugin-react-compiler@rc eslint-plugin-react-hooks@^6.0.0-rc.1

编译器是做什么的?

为了优化应用程序,React Compiler 会自动记住你的代码。您现在可能熟悉通过 API 进行记忆化,例如 、 和 。通过这些 API,你可以告诉 React 如果它们的输入没有改变,你的应用程序的某些部分就不需要重新计算,从而减少了更新的工作。虽然功能强大,但很容易忘记应用记忆化或错误地应用它们。这可能会导致更新效率低下,因为 React 必须检查没有任何有意义的更改的 UI 部分。useMemouseCallbackReact.memo

编译器利用其 JavaScript 和 React 规则的知识来自动记住组件和 hook 中的值或值组。如果它检测到规则的破坏,它将自动跳过这些组件或 hook,并继续安全地编译其他代码。

注意

React Compiler 可以静态检测何时违反 React 规则,并安全地选择退出仅优化受影响的组件或钩子。编译器没有必要优化 100% 的代码库

如果您的代码库已经很好地记住了,您可能不希望看到编译器的性能有重大改进。但是,在实践中,记住导致性能问题的正确依赖项是很棘手的。

深入探究

React Compiler 添加了什么样的记忆?

React Compiler 的初始版本主要专注于提高更新性能(重新渲染现有组件),因此它专注于以下两个用例:
1.跳过组件的级联重新渲染

  • 重新渲染会导致其组件树中的许多组件重新渲染,即使只有更改<Parent /><Parent />

2.跳过 React 外部的昂贵计算

  • 例如,在需要该数据的组件或 hook 内部调用expensivelyProcessAReallyLargeArrayOfObjects()
优化重新渲染

React 允许你将你的 UI 表示为当前状态的函数(更具体地说:它们的 props、state 和 context)。在当前的实现中,当组件的状态发生变化时,React 将重新渲染该组件及其所有子组件 — 除非你使用 、 或 应用了某种形式的手动记忆。例如,在下面的示例中,每当 的状态发生变化时,都会重新渲染:useMemo() useCallback() React.memo() <MessageButton><FriendList>

function FriendList({ friends }) {const onlineCount = useFriendOnlineCount();
if (friends.length === 0) {return <NoFriends />;}return (<div><span>{onlineCount} online</span>{friends.map((friend) => (<FriendListCard key={friend.id} friend={friend} />))}<MessageButton /></div>);
}

React Compiler 会自动应用等同于手动记忆化的作,确保只有应用程序的相关部分会随着状态变化而重新渲染,这有时被称为“细粒度响应性”。在上面的例子中,React 编译器确定 of 的返回值即使作为更改也可以重用,并且可以避免重新创建这个 JSX,并避免在 count 发生变化时重新渲染。<FriendListCard />friends<MessageButton>

昂贵的计算也会被记忆化

编译器还可以自动 memoize 渲染期间使用的昂贵计算:

// **Not** memoized by React Compiler, since this is not a component or hook
function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ }// Memoized by React Compiler since this is a component
function TableContainer({ items }) {// This function call would be memoized:const data = expensivelyProcessAReallyLargeArrayOfObjects(items);// ...
}

但是,如果确实是一个昂贵的函数,你可能需要考虑在 React 之外实现自己的记忆化,因为:expensivelyProcessAReallyLargeArrayOfObjects

  • React 编译器只记住 React 组件和钩子,而不是每个函数
  • React Compiler 的记忆不会在多个组件或 hook 之间共享

因此,如果用于许多不同的组件,即使传递了完全相同的项目,也会重复运行昂贵的计算。建议先进行分析,看看它是否真的那么昂贵,然后再使代码变得更复杂。expensivelyProcessAReallyLargeArrayOfObjects

开始使用

安装 eslint-plugin-react-hooks

React Compiler 还为 ESLint 插件提供支持。你可以通过安装 eslint-plugin-react-hooks@^6.0.0-rc.1 来试用。

npm install -D eslint-plugin-react-hooks@^6.0.0-rc.1

ESLint 插件将在编辑器中显示任何违反 React 规则的行为。当它这样做时,这意味着编译器跳过了优化该组件或 hook。这是完全可以的,编译器可以恢复并继续优化代码库中的其他组件。

你不必立即修复所有 ESLint 冲突。您可以按照自己的节奏解决它们,以增加要优化的组件和 hook 的数量,但不需要在使用编译器之前修复所有问题。

将编译器推出到您的代码库

现有项目

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

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

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

您对推出编译器更有信心时,您也可以将覆盖范围扩展到其他目录,并慢慢将其扩展到整个应用程序。

新项目

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

在 React 17 或 18 中使用 React 编译器

React 编译器与 React 19 RC 配合得最好。如果您无法升级,您可以安装额外的软件包,这将允许编译后的代码在 19 之前的版本上运行。但是,请注意,支持的最低版本为 17。react-compiler-runtime

npm install react-compiler-runtime@rc

你还应该将 correct 添加到你的编译器配置中,其中 是你目标的 React 的主要版本:target target

// babel.config.js
const ReactCompilerConfig = {target: '18' // '17' | '18' | '19'
};module.exports = function () {return {plugins: [['babel-plugin-react-compiler', ReactCompilerConfig],],};
}

在库上使用编译器

React Compiler 也可以用于编译库。因为 React Compiler 需要在任何代码转换之前在原始源代码上运行,所以应用程序的构建管道不可能编译它们使用的库。因此,建议库维护者使用编译器独立编译和测试他们的库,并将编译后的代码发送到 npm。

由于您的代码是预编译的,因此您的库的用户不需要启用编译器即可从应用于您的库的自动记忆化中受益。如果你的库面向尚未在 React 19 上运行的应用程序,请指定最小目标并将 react-compiler-runtime 添加为直接依赖项。运行时包将根据应用程序的版本使用正确的 API 实现,并在必要时对缺少的 API 进行 polyfill 填充。

库代码通常需要更复杂的模式和转义舱口的使用。因此,建议您确保进行足够的测试,以便识别在库上使用编译器可能出现的任何问题。如果你发现任何问题,你可以随时使用 'use no memo' 指令选择退出特定的组件或 hook。

与 app 类似,你不需要完全编译 100% 的组件或 hook 就能看到你的库里的好处。一个好的起点可能是确定库中对性能最敏感的部分,并确保它们不会违反 React 规则,你可以用它来识别。eslint-plugin-react-compiler

用法

Babel

npm install babel-plugin-react-compiler@rc

编译器包括一个 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 文档

Remix

安装 ,并向其添加编译器的 Babel 插件:vite-plugin-babel

npm install vite-plugin-babel
// vite.config.js
import babel from "vite-plugin-babel";const ReactCompilerConfig = { /* ... */ };export default defineConfig({plugins: [remix({ /* ... */}),babel({filter: /\.[jt]sx?$/,babelConfig: {presets: ["@babel/preset-typescript"], // if you use TypeScriptplugins: [["babel-plugin-react-compiler", ReactCompilerConfig],],},}),],
});

Webpack

社区 webpack loader已提供。

故障 排除

要报告问题,请首先在 React Compiler Playground 上创建一个最小重现,并将其包含在你的错误报告中。您可以在 facebook/react 存储库中打开问题。

你也可以通过申请成为成员在 React 编译器工作组中提供反馈。有关 加入的更多详细信息,请参阅 README。

编译器假定什么?

React 编译器假定你的代码:

  1. 是有效的语义 JavaScript。
  2. 测试在访问可空/可选值和属性之前是否定义它们(例如,如果使用 TypeScript,则通过启用 strictNullChecks ),即或使用 optional-chaining 。if (object.nullableProperty) { object.nullableProperty.foo }object.nullableProperty?.foo
  3. React 的规则。

React Compiler 可以静态验证 React 的许多规则,并在检测到错误时安全地跳过编译。要查看错误,建议同时安装 。eslint-plugin-react-compiler。

如何知道我的组件已优化?

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

编译后某些内容无法正常工作

如果你安装了 eslint-plugin-react-compiler,编译器会在你的编辑器中显示任何违反 React 规则的行为。当它这样做时,这意味着编译器跳过了优化该组件或 hook。这是完全可以的,编译器可以恢复并继续优化代码库中的其他组件。你不必立即修复所有 ESLint 冲突。您可以按照自己的节奏解决这些问题,以增加要优化的组件和 hook 的数量。

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

如果你的 app 在编译后无法正常工作,并且你没有看到任何 ESLint 错误,则编译器可能错误地编译了你的代码。为了确认这一点,请尝试通过 “use no memo” 指令主动选择退出您认为可能相关的任何组件或 hook,从而使问题消失。

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

注意
“use no memo”
"use no memo"是一个临时的转义舱口,允许你选择退出 React 编译器编译的组件和钩子。这个指令并不意味着像 use client 那样长寿。

除非绝对必要,否则不建议使用该指令。一旦你选择退出一个组件或 hook,它就会永远选择退出,直到该指令被删除。这意味着,即使您修复了代码,编译器仍将跳过编译它,除非您删除该指令。
#如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀

当您使错误消失时,请确认删除 opt out 指令会使问题再次出现。然后使用 React Compiler Playground 与React共享错误报告(您可以尝试将其简化为一个小的复制,或者如果它是开源代码,您也可以直接粘贴整个源代码),以便官方识别并帮助解决问题。

如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀

在这里插入图片描述


http://www.hkcw.cn/article/FxrJqmpsTh.shtml

相关文章

【机器学习基础】机器学习入门核心算法:K均值(K-Means)

机器学习入门核心算法&#xff1a;K均值&#xff08;K-Means&#xff09; 1. 算法逻辑2. 算法原理与数学推导2.1 目标函数2.2 数学推导2.3 时间复杂度 3. 模型评估内部评估指标外部评估指标&#xff08;需真实标签&#xff09; 4. 应用案例4.1 客户细分4.2 图像压缩4.3 文档聚类…

力扣热题100之二叉树的最大深度

题目 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 代码 方法一&#xff1a;递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightN…

【C++编程】C++学习笔记【更新ing】

C学习笔记 作者&#xff1a;齐花Guyc(CAUC) 文章目录 C学习笔记Chapter.1 面向对象编程&#xff08;OOP&#xff09;1.类&#xff08;class&#xff09;2.对象&#xff08;object&#xff09;3.封装&#xff08;Encapsulation&#xff09;4.继承&#xff08;Inheritance&#…

华为OD机试真题——矩形相交的面积(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

STM32F407VET6学习笔记7:Bootloader跳转APP程序

boot跳转APP的程序 目录 Flash分区设定&#xff1a; 工程文件地址设置&#xff1a; Bootloader工程文件&#xff1a; 测试的APP程序工程文件&#xff1a; Bootloader跳转程序&#xff1a; APP程序&#xff1a; Flash分区设定&#xff1a; 参考手册的分区&#xff1a; 工程文件…

5.29 打卡

DAY 39 图像数据与显存 知识点回顾 图像数据的格式&#xff1a;灰度和彩色数据模型的定义显存占用的4种地方 模型参数梯度参数优化器参数数据批量所占显存神经元输出中间状态 batchisize和训练的关系 作业&#xff1a;今日代码较少&#xff0c;理解内容即可 # 打印一张彩色图像…

关于scrapy在pycharm中run可以运行,但是debug不行的问题

关于scrapy在pycharm中run模式可以运行&#xff0c;但是debug模式不行的问题 文章目录 关于scrapy在pycharm中run模式可以运行&#xff0c;但是debug模式不行的问题查了下原因 点击run就可以运行&#xff0c;但是debug就是运行不了 一点击debug就报这个错&#xff0c;也不知道啥…

第7讲、Odoo 18 源码深度分析

Odoo 作为全球知名的开源 ERP 系统&#xff0c;其底层架构由众多核心 Python 文件共同支撑。本文将围绕 Odoo 18 版本中 的 api.py、exceptions.py、fields.py、http.py、loglevels.py、models.py、netsvc.py、release.py、sql_db.py 等关键文件&#xff0c;进行源码结构与实现…

【春秋云镜】CVE-2022-26965 靶场writeup

知识点 网站的主题或者模块位置一般是可以上传文件的&#xff0c;不过一般为压缩包形式主题或者模块可以上github上找到和cms匹配的源码主题被解压后会放到加入到对应的文件夹中&#xff0c;而且还会自动执行对应的info.php文件(需要主题和cms配套才行)我这里取巧了&#xff0…

JUC多线程核心知识点深度解析

最近正在复习Java八股&#xff0c;所以会将一些热门的八股问题&#xff0c;结合ai与自身理解写成博客便于记忆 本文将从以上10个经典面试问题来做juc多线程的解析 一、线程状态与流转机制 1. 六种线程状态&#xff08;Java定义&#xff09; public enum State {NEW, …

设计模式学习笔记

设计模式 一&#xff1a;分类&#xff1a; 创建型模式 用于描述“怎样创建对象”&#xff0c;它的主要特点是“将对象的创建与使用分离”。GoF&#xff08;四人组&#xff09;书中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。 结构型模式 用于描述如何将…

【地图】腾讯地图页面卡顿问题解决

目录 背景问题排查解决1. 页面是否使用 keep-alive 进行路由缓存2. 离开地图页面时&#xff0c;是否将地图清除 总结 背景 有的电脑没有显卡会出现如下问题&#xff1a; 系统打开有地图的页面&#xff0c;CPU 占用直线飙升到100%下不来&#xff0c;切到非地图页面&#xff0c;C…

一起看 I/O | Android 性能相关最新动态

作者 / Ben Weiss 过去几年来&#xff0c;我们一直致力于让性能提升工作变得更易上手、回报更高。我们将在本文中分享这一领域的最新发展动态。为您介绍基准配置文件、Android Studio 中的工具改进、库&#xff0c;以及我们如何让这项技术更好地在后台为您服务。此外&#xff0…

iPhone批量删除照片的方法

对于每一个iPhone用户来说&#xff0c;照片管理是一项日常而重要的任务。随着时间的积累&#xff0c;无数的照片快速填满了我们的存储空间&#xff0c;从美丽的风景到重要的家庭聚会&#xff0c;每一张照片都记录着我们生活中的瞬间。然而&#xff0c;当存储空间即将耗尽时&…

Gradle Kotlin 规范插件用于模块化结构 - 共享构建逻辑

Gradle Kotlin 规范插件用于模块化结构 - 共享构建逻辑 我们中的许多人都遇到过Groovy的困难&#xff0c;并习惯于将其转换为Kotlin DSL。 然后&#xff0c;作为Android工程师&#xff0c;在完全使用Kotlin编写的项目上工作是纯粹的喜悦。 我们假设采用基于功能的模块化应用程…

Gradle开发手册-高级篇之多模块项目创建

在进阶篇中详细讲解了gradle配置相关的详细内容。但是是基于单module的配置,在实际开发时基本全是多module类型的项目。所以本章我们就系统学习下如何构建多模块项目(父-子)以及相关的task内容。 基础篇:从概念以及广度上介绍下gradle的核心内容,并构建一个简单的java项目;…

Gradle的版本差异导致无法编译:Could not initialize class com.android.build.gradle.internal.TaskManager

运行项目报错:Could not initialize class com.android.build.gradle.internal.TaskManager 我这边的原因是少了SDK的包和JDK版本不对。 我们先区分下gradle version与gradle plugin version。如果对此不了解&#xff0c;经常会由于Gradle的版本号问题造成项目无法编译&#xf…

Gradle版本目录(Version Catalog)

Gradle版本目录(Version Catalog) “版本目录是一份依赖项列表&#xff0c;以依赖坐标表示&#xff0c;用户在构建脚本中声明依赖项时可以从中选择。” 我们可以使用版本目录将所有依赖项声明及其版本号保存在单个位置。这样&#xff0c;我们可以轻松地在模块和项目之间共享依…

android开发之NDK配置开发

1、打开项目后&#xff0c;一次点击Tools>SDK Manager 2、点击SDK Tools标签页 3、选中NDK&#xff08;Side by Side&#xff09;和CMake复选框 4、点击OK 此时系统会显示一个对话框&#xff0c;告诉你NDK软件包占用了多少磁盘空间 5、点击OK 6、安装完成后&#xff0c;点击…

如何在没有计算机的情况下将联系人从 iPhone 传输到安卓

如果您正在考虑从 iPhone 迁移到Android &#xff0c;您可能想知道如何在不丢失任何重要信息的情况下转移联系人。您可能还想避免使用电脑进行此过程。幸运的是&#xff0c;有几种方法可以教您如何在不使用电脑的情况下将联系人从 iPhone 迁移到Android &#xff0c;而且这些方…