🤖 作者簡介:水煮白菜王,一位前端勸退師 👻
👀 文章專欄: 前端專欄 ,記錄一下平時在博客寫作中,總結出的一些開發技巧和知識歸納總結?。
感謝支持💕💕💕
本文內容參考自 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 編譯器假定你的代碼:
- 是有效的語義 JavaScript。
- 測試在訪問可空/可選值和屬性之前是否定義它們(例如,如果使用 TypeScript,則通過啟用 strictNullChecks ),即或使用 optional-chaining 。
if (object.nullableProperty) { object.nullableProperty.foo }object.nullableProperty?.foo
- 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共享錯誤報告(您可以嘗試將其簡化為一個小的復制,或者如果它是開源代碼,您也可以直接粘貼整個源代碼),以便官方識別并幫助解決問題。