為什么會出現 React fiber架構
React 15 Stack Reconciler
是通過遞回更新子組件 ,由于遞回執行,所以更新一旦開始,中途就無法中斷,當層級很深時,遞回更新時間超過了16ms,用戶互動就會卡頓,

React16 Fiber Reconciler
通過把diff演算法分成很多小片,當一個小片執行完成時,由瀏覽器判斷是否有時間繼續執行新任務,沒時間就終止執行,有時間就檢查任務串列中有沒有新的、優先級更高的任務,有就做這個新任務,一直重復這個操作,

什么是 React fiber架構
簡單理解就是把一個耗時長的任務分解為一個個的作業單元(每個作業單元運行時間很短,不過總時間依然很長),在執行作業單元之前,由瀏覽器判斷是否有空余時間執行,有時間就執行作業單元,執行完成后,繼續判斷是否還有空閑時間,沒有時間就終止執行讓瀏覽器執行其他任務(如GUI執行緒等),等到下一幀執行時判斷是否有空余時間,有時間就從終止的地方繼續執行作業單元,一直重復到任務結束,
Fiber架構 = Fiber節點 + Fiber調度演算法
鏈表結構
要讓終止的任務恢復執行,就必須知道下一作業單元對應那一個,所以要實作作業單元的連接,就要使用鏈表,在每個作業單元中保存下一個作業單元的指標,就能恢復任務的執行,
requestIdleCallback
要知道每一幀的空閑時間,就需要使用 requestIdleCallback Api,傳入回呼函式,回呼函式接收一個引數(剩余時間),如果有剩余時間,那么就執行作業單元,如果時間不足了,則繼續requestIdleCallback,等到下一幀繼續判斷,
React 中的 React fiber架構
資料結構
使用 Fiber節點, 來代替虛擬DOM原來的結構,
// 鏈表結構
export type Fiber = {
// Fiber 型別資訊
type: any,
// 跟當前Fiber相關本地狀態(比如瀏覽器環境就是DOM節點)
stateNode: any,
...
// 指向父節點,或者render該節點的組件
return: Fiber | null,
// 指向第一個子節點
child: Fiber | null,
// 指向下一個兄弟節點
sibling: Fiber | null,
}

簡介協調階段
通過ReactDOM.render() 和 setState 把待更新的任務會先放入佇列中, 然后通過 requestIdleCallback 請求瀏覽器調度,
// 更新節點 放入陣列中
updateQueue.push(updateTask);
requestIdleCallback(performWork, {timeout});
現在瀏覽器有空閑或者超時了就會呼叫performWork來執行任務:
// performWork 會拿到一個Deadline,表示剩余時間
function performWork(deadline) {
// 回圈取出updateQueue中的任務
while (updateQueue.length > 0 && deadline.timeRemaining() > ENOUGH_TIME) {
workLoop(deadline);//
}
// 如果在本次執行中,未能將所有任務執行完畢,那就再請求瀏覽器調度
if (updateQueue.length > 0) {
requestIdleCallback(performWork);
}
}
這里的nextUnitOfWork下一個作業單元是Fiber結構,所以終止了之后也能恢復繼續執行,
// 保存當前的處理現場
let nextUnitOfWork: Fiber | undefined // 保存下一個需要處理的作業單元
let topWork: Fiber | undefined // 保存第一個作業單元
function workLoop(deadline: IdleDeadline) {
// updateQueue中獲取下一個或者恢復上一次中斷的執行單元
if (nextUnitOfWork == null) {
nextUnitOfWork = topWork = getNextUnitOfWork();
}
// 每執行完一個執行單元,檢查一次剩余時間
// 如果被中斷,下一次執行還是從 nextUnitOfWork 開始處理
while (nextUnitOfWork && deadline.timeRemaining() > ENOUGH_TIME) {
// 處理節點 并 回傳下一個 要處理得節點
nextUnitOfWork = performUnitOfWork(nextUnitOfWork, topWork);
}
// 提交作業,當任務全部執行完后 一次全部更新 同步執行
if (pendingCommit) {
// commit 階段
commitAllWork(pendingCommit);
}
}
/**
* 回傳下一個 要處理的 nextUnitOfWork
* @params fiber 當前需要處理的節點
* @params topWork 本次更新的根節點
*/
function performUnitOfWork(fiber: Fiber, topWork: Fiber) {
// 對該節點進行處理
// diff演算法 為修改的節點打上標簽
// 在fiber上 生成對應的stateNode (真實的DOM節點)
beginWork(fiber);
// 如果存在子節點,那么下一個待處理的就是子節點
if (fiber.child) {
return fiber.child;
}
// 沒有子節點了,上溯查找兄弟節點
let temp = fiber;
while (temp) {
completeWork(temp);// 收集副作用函式 commit 階段執行
// 到頂層節點了, 退出
if (temp === topWork) {
break
}
// 找到,下一個要處理的就是兄弟節點
if (temp.sibling) {
return temp.sibling;
}
// 沒有, 繼續上溯
temp = temp.return;
}
}
diff演算法 對比步驟

簡介渲染階段
協調階段完成后生成了 WorkInProgress Tree,在有修改的Fiber節點中都有一個標簽,在Renderer 階段回圈 WorkInProgress Tree進行修改節點然后渲染到頁面上,
// 任務都執行完后 進入commit 修改真實Tree
function commitAllWork(fiber) {
if(!fiber) {
return;
}
const parentDom = fiber.return.dom;
if(fiber.effectTag === 'REPLACEMENT' && fiber.dom) {
parentDom.appendChild(fiber.dom);
} else if(fiber.effectTag === 'DELETION') {
parentDom.removeChild(fiber.dom);
} else if(fiber.effectTag === 'UPDATE' && fiber.dom) {
// 更新DOM屬性
updateDom(fiber.dom, fiber.alternate.props, fiber.props);
}
// 遞回操作子元素和兄弟元素
commitRootImpl(fiber.child);
commitRootImpl(fiber.sibling);
}
參考文章
React 技術揭秘
這可能是最通俗的 React Fiber(時間分片) 打開方式
Deep In React 之淺談 React Fiber 架構(一)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/246532.html
標籤:其他
上一篇:redis學習總結
下一篇:微服務架構下的子服務器記憶體波動
