深入理解React的Fiber架構(gòu)
Fiber 的核心目標(biāo)——把一次“不可中斷的遞歸渲染”,拆成“可中斷、可恢復(fù)、可優(yōu)先級(jí)調(diào)度的工作單元”
一、為什么 React 要引入 Fiber?
老架構(gòu)(Stack Reconciler)的問(wèn)題,React 16 之前:
- diff + render 是一次性同步遞歸
- JS 線(xiàn)程被長(zhǎng)期占用
- 瀏覽器:
- ? 不能響應(yīng)用戶(hù)輸入
- ? 不能渲染動(dòng)畫(huà)
- ? 會(huì)卡頓、掉幀
?? 本質(zhì)問(wèn)題:React 沒(méi)法“暫停 / 打斷 / 恢復(fù)”渲染
二、核心概念
Fiber 是 React 內(nèi)部對(duì)“組件/DOM 節(jié)點(diǎn)”的一種可調(diào)度的執(zhí)行單元(Work Unit)
不是虛擬 DOM 本身,而是:
- 虛擬 DOM + 運(yùn)行時(shí)控制信息
- 數(shù)據(jù)結(jié)構(gòu) + 執(zhí)行上下文
三、Fiber 的核心數(shù)據(jù)結(jié)構(gòu)
一個(gè) Fiber 本質(zhì)是一個(gè)對(duì)象:
interface Fiber {
// 節(jié)點(diǎn)類(lèi)型
tag: WorkTag;
// 對(duì)應(yīng)的組件或 DOM
type: any;
stateNode: any;
// Fiber 樹(shù)結(jié)構(gòu)
return: Fiber | null; // 父
child: Fiber | null; // 第一個(gè)子
sibling: Fiber | null; // 下一個(gè)兄弟
// 狀態(tài)相關(guān)(Hooks 在這)
memoizedState: any;
updateQueue: UpdateQueue<any> | null;
// props
pendingProps: any;
memoizedProps: any;
// 副作用
flags: Flags;
subtreeFlags: Flags;
// 雙緩存
alternate: Fiber | null;
}
一些關(guān)鍵點(diǎn):
- 樹(shù)結(jié)構(gòu)是 鏈表式的,為了能中斷遍歷,而不是遞歸調(diào)用棧
child → sibling → sibling
alternate:Fiber 的靈魂,“并發(fā)渲染不撕裂 UI”的關(guān)鍵
current Fiber ←→ workInProgress Fiber
- current:當(dāng)前屏幕顯示的
- wip:正在計(jì)算的新版本
- Hooks 存在哪里?useState 不是存在閉包,而是存在 Fiber 上
fiber.memoizedState // Hooks 單向鏈表
四、Fiber 架構(gòu)下的兩大階段
這兩個(gè)階段正是組件渲染的兩個(gè)階段:
- 父組件渲染導(dǎo)致子組件渲染(默認(rèn)行為)
- 組件內(nèi)狀態(tài)更新
- context 改變導(dǎo)致使用 useContext 的組件更新
1?? Render 階段(Reconciliation 協(xié)調(diào)階段)
做什么?
- 創(chuàng)建 / 復(fù)用 Fiber(diff 過(guò)程)
- 執(zhí)行函數(shù)組件
- 計(jì)算新 state,運(yùn)行 useState,useMemo,useCallback
- 標(biāo)記副作用(flags),生成更新鏈表
特點(diǎn):
- ? 可打斷
- ? 不操作 DOM
- ? 不執(zhí)行副作用
beginWork
↓
completeWork
2?? Commit 階段(提交階段 / 實(shí)際 DOM 操作)
分三步:
- before mutation(提交前)
執(zhí)行:
- getSnapshotBeforeUpdate
- 調(diào)整內(nèi)容前的準(zhǔn)備
- mutation(變更 DOM)
執(zhí)行所有 DOM 操作:
- 插入 DOM
- 刪除 DOM
- 修改 DOM 屬性
- ref 的更新
- layout(布局階段)
執(zhí)行 Layout Effects:
- useLayoutEffect 回調(diào)
- class 組件的 componentDidMount / componentDidUpdate
Mutation 和 Layout 這兩步是同步執(zhí)行的,不允許中斷。
總結(jié)下,上述做了什么?
- 更新 DOM
- 執(zhí)行 useEffect / useLayoutEffect(componentDidMount)
- ref 賦值
特點(diǎn):
- ? 不可中斷
- ? 必須一次完成
Render 階段算賬,Commit 階段交付
state 更新 / props 更新 / context 更新
↓
---- Render 階段(可中斷) ----
1. 執(zhí)行組件函數(shù)
2. 運(yùn)行 hooks
3. 生成新的 Fiber 樹(shù)
4. 計(jì)算 diff
5. 構(gòu)建 effectList(DOM 變更清單)
↓
---- Commit 階段(不可中斷) ----
1. before mutation(DOM 操作前的準(zhǔn)備)
2. mutation(真實(shí) DOM 更新)
3. layout(執(zhí)行 useLayoutEffect、componentDidMount)
↓
瀏覽器繪制(paint)
↓
最后執(zhí)行 useEffect(異步)
五、Fiber 是如何實(shí)現(xiàn)“可中斷”的?
核心思想:時(shí)間切片(Time Slicing)
React 不再:
renderTree(); // 一口氣干完
而是:
while (還有時(shí)間 && 還有任務(wù)) {
執(zhí)行一個(gè) Fiber
}
如果:
- 瀏覽器要繪制
- 用戶(hù)輸入
- 高優(yōu)先級(jí)任務(wù)來(lái)了
那么,React 暫停當(dāng)前 Fiber,稍后恢復(fù)。
Fiber 為什么能恢復(fù)?
因?yàn)?Fiber 本身就保存了:
- 執(zhí)行到哪了
- 子節(jié)點(diǎn)處理到哪
- 副作用信息
六、優(yōu)先級(jí)調(diào)度(Scheduler)
不同更新優(yōu)先級(jí)不同:
React 可以:
- 丟棄低優(yōu)先級(jí)渲染
- 重做高優(yōu)先級(jí)渲染
- 但 UI 不會(huì)亂
七、一次 setState 的 Fiber 流程
setCount(c => c + 1);
內(nèi)部流程:
- 創(chuàng)建 update
- 掛到 Fiber.updateQueue
- 根據(jù)優(yōu)先級(jí)調(diào)度
- 創(chuàng)建 workInProgress Fiber
- render 階段計(jì)算新 state(state 永遠(yuǎn)只在 Fiber 上變)
- 標(biāo)記 flags
- commit 階段更新 DOM
八、和 Vue 響應(yīng)式的根本差異
Vue:數(shù)據(jù)驅(qū)動(dòng)更新 React:調(diào)度驅(qū)動(dòng)更新
Vue:
state.x = 1 → 精確更新
React:
setState → 重新執(zhí)行組件
Fiber 的存在,是 React 能做到并發(fā)的前提。
九、如何實(shí)現(xiàn)“并發(fā)”?
React 并發(fā)(Concurrent)不是多線(xiàn)程,而是:?在單線(xiàn)程 JS 中,把一次渲染拆成可中斷、可重試、可丟棄的任務(wù)調(diào)度模型。
?? React 并發(fā) ≠ Promise ≠ async/await ≠ Web Worker
并發(fā)的是 “渲染任務(wù)的調(diào)度權(quán)”,React 可以在多個(gè)“未完成的渲染版本”之間來(lái)回切換。
本質(zhì)上就是通過(guò) 雙 Fiber 樹(shù) + render/commit 分離 + scheduler 調(diào)度器 ?實(shí)現(xiàn)的。
并發(fā)特性?是否生效?,取決于:
- useTransition
- useDeferredValue
- Suspense
- 更新優(yōu)先級(jí)
scheduler 調(diào)度器 體現(xiàn)在開(kāi)發(fā)層面是指 useTransition、useDeferredValue、Suspense 等。
這些都是并發(fā)
1?? 可中斷(Interruptible)
渲染到一半 →
瀏覽器要繪制 →
React 暫停 render
2?? 可重試(Restartable)
低優(yōu)先級(jí)渲染中 →
高優(yōu)先級(jí)更新來(lái)了 →
丟棄當(dāng)前渲染 →
從頭再算
3?? 可丟棄(Discardable)
用戶(hù)連續(xù)輸入 →
前面幾次渲染直接廢棄
最終只 commit“最后一次正確結(jié)果”
到此這篇關(guān)于深入理解React的Fiber架構(gòu)的文章就介紹到這了,更多相關(guān)React Fiber架構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入理解React Fiber機(jī)制
- React 中的雙緩存 Fiber 樹(shù)機(jī)制詳解
- React Fiber 架構(gòu)解決頁(yè)面卡頓問(wèn)題的全過(guò)程
- React Fiber中面試官最關(guān)心的技術(shù)話(huà)題
- React-three-fiber使用初體驗(yàn)
- React超詳細(xì)講述Fiber的使用
- React?Fiber構(gòu)建源碼解析
- React?Fiber構(gòu)建completeWork源碼解析
- React?Fiber原理深入分析
- React Fiber與調(diào)和深入分析
- React Fiber源碼深入分析
- React的Fiber的實(shí)現(xiàn), Vue為什么不需要Fiber
相關(guān)文章
React報(bào)錯(cuò)map()?is?not?a?function詳析
這篇文章主要介紹了React報(bào)錯(cuò)map()?is?not?a?function詳析,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
react 通過(guò)后端接口實(shí)現(xiàn)路由授權(quán)的示例代碼
本文主要介紹了React應(yīng)用中通過(guò)后端接口獲取路由授權(quán),實(shí)現(xiàn)動(dòng)態(tài)和靈活的權(quán)限管理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11
React組件實(shí)例三大屬性state props refs使用詳解
這篇文章主要為大家介紹了React組件實(shí)例三大屬性state props refs使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
React-View-UI組件庫(kù)封裝Loading加載中源碼
這篇文章主要介紹了React-View-UI組件庫(kù)封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測(cè)試源碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

