介紹
多年來,React 開發人員一直使用 useMemo、useCallback、React.memo 和各種狀態管理啟發式方法手動優化重新渲染。 React Compiler(以前稱為 React Forget)完全改變了這個範例。它是一個建置時工具,透過在編譯時分析反應依賴關係來自動記憶 React 元件、鉤子和值,從而無需手動最佳化鉤子。
手動記憶的問題
考慮一個典型的過濾列表元件:
function UserList({ users, filter }) {
const filtered = useMemo(
() => users.filter(u => u.name.includes(filter)),
[users, filter]
);
const handleClick = useCallback((id) => {
console.log('Clicked:', id);
}, []);
return (
<ul>
{filtered.map(user => (
<UserRow key={user.id} user={user} onClick={handleClick} />
))}
</ul>
);
}
const UserRow = React.memo(({ user, onClick }) => (
<li onClick={() => onClick(user.id)}>{user.name}</li>
));
這是可行的,但它很冗長,容易出錯(依賴項數組遺失或不正確),並且很容易忘記。大列表中缺少 useMemo 會導致不必要的重新渲染;不正確的依賴數組會導致陳舊的閉包。
什麼是 React 編譯器?
React Compiler 是一個建置時插件(Babel / SWC),可以自動理解 React 元件的反應性模型。它會自動轉譯您的程式碼以包含記憶邏輯:
// You write this:
function Welcome({ name }) {
const greeting = `Hello, ${name}!`;
return <p>{greeting}</p>;
}
// Compiler outputs effectively this:
function Welcome({ name }) {
const $ = React.cache(/* ... */);
let greeting = $.refresh('greeting', name => `Hello, ${name}!`, [name]);
return $.memo('element', () => <p>{greeting}</p>, [greeting]);
}
編譯器會偵測哪些值依賴 props、state 或其他反應值,並且僅在這些依賴項發生變化時重新計算它們。
它是如何運作的
編譯器分三個階段運作:
反應性值偵測 — 它遍歷抽象語法樹 (AST),以識別哪些變數源自 props、state、hooks 或其他反應性來源。
依賴圖建構 — 它建立連接反應值的依賴圖。任何僅源自非反應源的值均被視為靜態值。
記憶化程式碼產生 — 它產生具有內建快取失效功能的最佳化 JavaScript。運作時使用類似訊號的細粒度依賴追蹤機制。
編譯器遵循的關鍵規則:
| 規則 | 行為 |
|---|---|
| Props 和 state 總是反應性的 | 任何派生值都會自動記憶 |
| 非反應常數被提升 | 沒有增加記憶開銷 |
| 回呼預設穩定 | 不需要 useCallback |
| 元件主體最佳化 | 僅在反應性依賴發生變化時才重新執行 |
與建置工具集成
維特
// vite.config.js
import react from '@vitejs/plugin-react';
export default {
plugins: [
react({
babel: {
plugins: [['babel-plugin-react-compiler', { target: '18' }]],
},
}),
],
};
Next.js
// next.config.js
module.exports = {
experimental: {
reactCompiler: true,
},
};
你可以刪除什麼
啟用 React 編譯器後,您不再需要:
useMemo()— 自動值記憶useCallback()— 自動功能穩定性React.memo()— 自動元件記憶(編譯器包裝渲染輸出)- 大多數情況下手動依賴數組
編譯器甚至可以優化難以手動處理的模式,例如條件掛鉤 - 儘管您仍然應該遵循掛鉤規則。
注意事項和最佳實踐
雖然編譯器可以處理絕大多數情況,但某些情況需要謹慎:
| 警告 | 推薦 |
|---|---|
可變引用 — useRef 突變未被追蹤 | 使用 useState 或觸發明確重新渲染 |
| 外部儲存 — 值不是來自 React | 包裹在 useSyncExternalStore |
複雜的副作用 — useEffect 具有非反應性依賴 | 如果需要,添加明確依賴 |
| 遺留上下文 — 類別元件上下文 | 遷移到現代 useContext |
此編譯器對於遵循標準 React 模式的程式碼庫最為有效。高度命令式程式碼可能帶來的好處較少。
結論
React 編譯器代表了從手動到自動記憶的根本轉變。透過在建置時分析反應性依賴關係,它消除了整個類別的效能錯誤和樣板檔案。對於現有項目,啟用編譯器需要最少的配置;對於新項目,它改變了你對 React 效能的看法——你只需編寫聲明性元件,然後讓編譯器處理其餘的事情。

