上一篇《知根知底setState》介紹了在不一樣場景調用setState
會有不一樣的處理過程,但無論怎樣最終目的都是要進行react的更新,本文咱們就來了解setState以後的更新過程。
提示:源碼基於react v16.4.3-alpha.0react
字面上意思能夠理解爲查找擁有最高優先級的root,這個root又是什麼呢?git
// react 項目的入口文件,初始化
ReactDom.render(
<App />,
document.getElementById('app')
)
複製代碼
root就是經過react生成的節點樹的根節點 <div id="app"></div>
做用:
1,找到擁有最高優先級的root
2,從更新任務鏈表中刪除沒有更新任務的root。這個沒有更新任務的root的怎麼理解呢——本來這個root的其中某個子節點觸發了更新,可是更新已經被處理完了,因此root.expirationTime === NoWork
將已處理完成的去除掉github
在findHighestPriorityRoot函數中能夠發現 firstScheduledRoot
,lastScheduledRoot
表示一個任務調度鏈表(環形)的頭結點和尾節點,那這個鏈表在哪裏生成的呢——addRootToSchedule源碼bash
總結前兩個函數的功能:app
addRootToSchedule的調用位置就是在requestWork源碼
看過《知根知底setState》的同窗能夠知道在將setState的時候到這裏就結束了,爲何呢?由於接下來就是react fiber的Reconciler和Commit階段。異步
下面進入本文的主題了react fiber,從requestWork開始函數
源碼oop
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
addRootToSchedule(root, expirationTime);
...
if (expirationTime === Sync) {
// 同步更新
performSyncWork();
} else {
// 異步更新
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}
複製代碼
根據expirationTime判斷採用同步更新仍是異步更新,本文主要從同步更新的過程來了解fiberpost
// function performSyncWork() {
performWork(Sync, null);
}
複製代碼
performSyncWork僅僅只是調用performWork,注意第二個參數爲nullui
function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
deadline = dl;
// 保證每一次進行正在處理的更新任務都是優先級最高的
findHighestPriorityRoot()
if (deadline !== null) {
// 處理同步更新
// 在當前找到的優先級最高的root上開始後續的更新工做
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime >= nextFlushedExpirationTime,
);
} else {
// 處理異步更
}
}
複製代碼
從代碼中的if/else
能夠發現雖然在requestWork
中同步和異步走向兩個不一樣的分支,但最後又會合併到同一個處理過程performWork
準備好小板凳和瓜子終於到了fiber的正頭戲了,源碼
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isExpired: boolean,
) {
// 一樣有兩個分支——同步和異步
if (deadline === null || isExpired) {
// 同步的處理邏輯
// 咱們知道fiber的Reconciler階段到Commit階段是能夠被中斷的
// finishedWork就是Reconciler階段diff出來的反作用(新state生成的DOM)
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// 不爲空說明以前有任務被中斷,須要如今處理完被中斷的任務
completeRoot(root, finishedWork, expirationTime);
} else {
// 沒有被中斷的任務,就處理當前找到的優先級最高的更新任務
root.finishedWork = null;
// 是否能夠被中斷,對於同步和異步到期work不可被中斷
const isYieldy = false;
// Reconciler階段
renderRoot(root, isYieldy, isExpired);
// finishedWork其實就是rootWorkInProgress(新的VNode)
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// diff以後獲得的結果是:有更新,
// 進入到Commit階段
completeRoot(root, finishedWork, expirationTime);
}
}
} else {
// 異步的處理邏輯
}
}
複製代碼
流程圖:
function renderRoot(
root: FiberRoot,
isYieldy: boolean,
isExpired: boolean,
): void {
...
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null
) {
// 檢查咱們是否從一個新堆棧開始,或者咱們是不是從以前的工做中恢復
...
}
do {
try {
workLoop(isYieldy);
} catch (thrownValue) {
...
}
// 執行一次就跳出循環
break;
} while (true);
}
複製代碼
其實從執行setState
到workLoop
若是你能理解,fiber的大概過程你也就理解了。workLoop以後的主要邏輯是進行diff——調用render前生命週期函數,應用新的state執行render獲得新的DOM,將要更新的信息掛在到rootWorkInProgress,並最後賦值給finishedWork。
這個過程的代碼量比較大,react大多數功能都是在這個階段進行的(如:ref,context等),固然這部分代碼邏輯比較直白式的理解起來也相對簡單,若是有興趣能夠自行研究下
function completeRoot(
root: FiberRoot,
finishedWork: Fiber,
expirationTime: ExpirationTime,
): void {
...
commitRoot(root, finishedWork);
}
複製代碼
function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
...
// 執行render以後的生命週期
// 應用新state生成的Element(在diff階段經過createElement等建立)到DOM樹種
}
複製代碼
這個階段的邏輯起來也比較簡單,對這塊感興趣的同窗能夠自行研究下
結合《知根知底setState》和本文咱們粗略介紹了react v16的更新過程。
你可能會發現本文對一些實現細節沒有作過多的描述
這是由於對於fiber實現細節介紹的文章網上已經能夠搜索出不少,並且講的也比較詳細。
本文主要在同步更新的過程,異步更新和同步更新大多處理邏輯都是通用的,簡單說就以不一樣的方式進入到更新過程,並且react v16的源碼各版本依舊在變更。
我寫這篇的目的:
對於想去閱讀源碼的人爲他們提供一個邏輯流程,箇中細節他們能夠本身慢慢研究(固然這也不是經過一篇兩篇文章能描述完整的)
對於只是想了解下內部原理的人省去了理解大量細節處理的煩惱