iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0
Modern Web

前進React 生態系 : 技術應用與概念解析系列 第 25

Day 25 - React Compiler 原理解析

  • 分享至 

  • xImage
  •  

React Compiler 是怎麼運作的?

React Compiler 的運作流程很複雜,概括來說會執行以下幾個步驟:

  1. Babel Plugin: Babel Plugin 會根據設定,選擇需要編譯的 component 或 hook。之後會被交給編譯器處理,並接收到一個轉換後的 AST 節點。
  2. Lowering : 將 Babel AST 轉換為 HIR (High-level Intermediate Representation),讓程式碼更容易進行分析和優化。
  3. SSA 轉換:編譯器會把 HIR 轉換成 SSA (Static Single Assignment, 靜態單賦值形式),這有助於後續的優化。
  4. 驗證 : 檢查是否是有效的 React 程式碼,並檢查違反 React 規則。像是是否在條件式使用 Hook 之類的。
  5. 優化 : 編譯器會執行各種優化操作,像是刪除無效的程式碼或常數傳播(constant propagation)。
  6. 型別推斷:編譯器會執行型別推斷,識別出程式中的一些關鍵的數據型別(如 hooks、primitives 等),以便後續的分析。
  7. 推斷 Reactive Scopes:編譯器會分析哪些變數是一起創建或修改的,並找出相關的指令集合,這些集合被稱為 「reactive scopes」。
  8. 構建/優化 Reactive Scopes:編譯器將這些確定的 reactive scopes 轉換成程式,並在 HIR 中明確地顯示這些範圍。接著再轉換成一個 "ReactiveFunction",這是一種 HIR 與 AST 的混合表示形式。之後 reactive scopes 會再被修剪和轉換。
  9. 生成程式碼:編譯器將 ReactiveFunction 的 HIR/AST 混合表示形式轉換回 Babel 的 AST。
  10. AST 節點替換:Babel Plugin 會用新的 AST 節點取代原本的程式碼,完成編譯與優化的過程。

名詞解釋:AST、HIR、SSA 與常數傳播

Abstract Syntax Tree (抽象語法樹) 是什麼?

抽象語法樹 (AST) 是一種樹狀結構,用來表示程式碼的語法結構。如果好奇怎麼呈現的可以使用 AST Explorer 來觀察。

HIR (High-level Intermediate Representation) 是什麼?

首先先介紹 控制流圖 (Control-Flow Graph,CFG),控制流圖是一種圖形表示法,用來表示程式在執行過程中可能走訪的所有路徑。

控制流圖能幫助編譯器理解程式的執行路徑,而將抽象語法樹(AST)轉換成 HIR 的過程稱為 Lowering,而 HIR 是一種呈現控制流圖的方式,呈現方式會是用一個個區塊 (block) 組成。

SSA (Static Single Assignment Form) 是什麼?

SSA 是一種表示方式,確保每個變數在程式中只被賦值一次,可以讓編譯器能更容易進行優化。

簡易範例說明:

let a = 5;
let b = a + 2;

在 SSA 形式下,可能會被表示成這樣:

a1 = 5
b1 = a1 + 2

a1 和 b1 會是唯一的變數名稱,

常數傳播 (Constant propagation) 是什麼?

常數傳播是一種優化技術,將變數替換為已知的常數值來提高程式的執行效率。

這是一個簡單的範例:

function calculateTotal(price: number) {
  const taxRate = 0.1; // 這是一個常數
  const total = price + price * taxRate;
  return total;
}

會轉換成

function calculateTotal(price: number) {
  const total = price + price * 0.1; // 直接使用常數
  return total;
}

實際範例

假設有一個簡單 Component

export default function MyApp() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Add</button>
    </div>
  );
}

透過 React Compiler 最後會被轉換成這樣

function MyApp() {
  const $ = _c(2);
  const [count, setCount] = useState(0);
  let t0;
  // 檢查快取的第一個項目是否與 count 相同
  if ($[0] !== count) {
    t0 = (
      <div>
        <p>{count}</p>
        <button onClick={() => setCount(count + 1)}>Add</button>
      </div>
    );
    // 更新快取的第一個項目為當前的 count
    $[0] = count;
    // 更新快取的第二個項目為當前渲染的內容
    $[1] = t0;
  } else {
    // 如果相同,則直接使用快取的渲染結果
    t0 = $[1];
  }
  return t0;
}

在裡面有一個函數是 _c,實際上使用的是 useMemoCache 的函數。useMemoCache 的函數是 React Compiler 內部使用的,用來管理 memoization 所用的快取。

更詳細的細節可以在 React Playground 進行測試,查看每一個流程的結果。

參考資料:
https://github.com/facebook/react/blob/main/compiler/docs/DESIGN_GOALS.md#architecture
https://yongseok.me/blog/en/react_compiler_1/
https://yongseok.me/blog/en/react_compiler_2/
https://yongseok.me/blog/en/react_compiler_3/
https://yongseok.me/blog/en/react_compiler_4/
https://www.youtube.com/watch?v=PYHBHK37xlE
https://www.youtube.com/watch?v=uA_PVyZP7AI
https://en.wikipedia.org/wiki/Control-flow_graph
https://zh.wikipedia.org/zh-tw/%E9%9D%99%E6%80%81%E5%8D%95%E8%B5%8B%E5%80%BC%E5%BD%A2%E5%BC%8F


上一篇
Day 24 - React Compiler 簡介
下一篇
Day 26 - React Fiber 和 Concurrent 原理解析
系列文
前進React 生態系 : 技術應用與概念解析30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言