React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態)
React Fiber源碼分析 第四篇(概括總結)node
React Fiber是React在V16版本中的大更新,利用了閒餘時間看了一些源碼,作個小記錄~
若是有錯誤,請輕噴react
1.scheduleRootUpdate這個函數主要執行了兩個操做 1個是建立更新createUpdate並放到更新隊列enqueueUpdate, 1個是執行sheculeWork函數segmentfault
function scheduleRootUpdate(current$$1, element, expirationTime, callback) { var update = createUpdate(expirationTime); update.payload = { element: element }; callback = callback === undefined ? null : callback; if (callback !== null) { update.callback = callback; } enqueueUpdate(current$$1, update); scheduleWork(current$$1, expirationTime); return expirationTime; }
2.先從createUpdate函數分析, 他直接返回了一個包含了更新信息的對象app
function createUpdate(expirationTime) { return { // 優先級 expirationTime: expirationTime, // 更新類型 tag: UpdateState, // 更新的對象 payload: null, callback: null, // 指向下一個更新 next: null, // 指向下一個更新effect nextEffect: null }; }
3.接着更新payload和callback屬性, payload即爲更新的對象, 而後執行enqueuUpdate, enqueueUpdate相對比較容易理解, 不過裏面有一註釋挺重要less
Both queues are non-empty. The last update is the same in both lists, because of structural sharing. So, only append to one of the lists 意思是alternate的updateQueue和fiber的updateQueue是同一個對象引用,這裏會在createWorkInProcess提到
往下走就是重要的scheduleWork, 它是render階段真正的開始異步
function scheduleWork(fiber, expirationTime) { // 更新優先級 var root = scheduleWorkToRoot(fiber, expirationTime); ...if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime < nextRenderExpirationTime) { // This is an interruption. (Used for performance tracking.) 若是這是一個打斷原有更新的任務, 先把現有任務記錄 interruptedBy = fiber; resetStack(); } // 設置下一個操做時間nextExpirationTimeToWorkOn markPendingPriorityLevel(root, expirationTime); if ( // If we're in the render phase, we don't need to schedule this root // for an update, because we'll do it before we exit... !isWorking || isCommitting$1 || // ...unless this is a different root than the one we're rendering. nextRoot !== root) { var rootExpirationTime = root.expirationTime; requestWork(root, rootExpirationTime); } ... }
4.scheduleWork先執行一個scheduleWorkToRoot函數, 該函數主要是更新其expirationTime以及上層fiber的childrenExpirationTimeasync
function scheduleWorkToRoot(fiber, expirationTime) { // Update the source fiber's expiration time if (fiber.expirationTime === NoWork || fiber.expirationTime > expirationTime) { fiber.expirationTime = expirationTime; } var alternate = fiber.alternate; if (alternate !== null && (alternate.expirationTime === NoWork || alternate.expirationTime > expirationTime)) { alternate.expirationTime = expirationTime; } // 若是是HostRoot 即直接返回 var node = fiber.return; if (node === null && fiber.tag === HostRoot) { return fiber.stateNode; } // 若子fiber中有更新, 即更新其childrenExpirationTime while (node !== null) { ... } return null; }
5.接着會執行一個markPendingPriorityLevel函數,這個函數主要是更新root的最高優先級和最低優先級(earliestPendingTime和lastestPendingTime;), 同時設置下一個執行操做的時間nextExpirationTimeToWorkOn(即root中具備最高優先級的fiber的expirationTime),關於這個函數的latestSuspendedTime;之後再說函數
最後scheduleWork會執行requestWorkoop
function requestWork(root, expirationTime) { addRootToSchedule(root, expirationTime); if (isRendering) { // rendering狀態,直接返回 return; } if (isBatchingUpdates) { // isBatchingUpdates, 直接返回。 react的state更新是會合並的 ...return; } // TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { // 執行同步 performSyncWork(); } else { // 異步, 暫不分析 scheduleCallbackWithExpirationTime(root, expirationTime); } }
6.requestWork 會先執行addRootToSchedule,由函數名稱可知其做用,將root加到schedule, 即設置firstScheduledRoot, lastScheduledRoot以及他們的nextScheduleRoot屬性,說白了就是一個閉環鏈式結構 first => next => next => last(next => first), 同時更新root的expirationTime屬性源碼分析
function addRootToSchedule(root, expirationTime) { // root還沒有開始過任務 將root加到schedule if (root.nextScheduledRoot === null) { ... } else { // root已經開始執行過任務, 更新root的expirationTime var remainingExpirationTime = root.expirationTime; if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) { root.expirationTime = expirationTime; } } }
7.接着requestWork會判斷是否正在渲染中,防止重入。剩餘的工做將安排在當前渲染批次的末尾,若是正在渲染直接返回後, 由於已經把root加上到Schedule裏面了,依然會把該root執行
同時判斷是否正在batch update, 這裏留到分析setState的時候說, 最後根據異步或者同步執行不一樣函數, 此處執行同步performSyncWork(),performSyncWork直接執行performWork(Sync, null);
function performWork(minExpirationTime, dl) { deadline = dl; // 找出優先級最高的root findHighestPriorityRoot(); if (deadline !== null) { // ...異步 } else { // 循環執行root任務 while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime)) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true); findHighestPriorityRoot(); } } ... // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime); } ... }
8.performWork首先執行findHighestPriorityRoot函數。findHighestPriorityRoot函數主要執行兩個操做, 一個是判斷當前root是否還有任務,若是沒有, 則從firstScheuleRoot鏈中移除。 一個是找出優先級最高的root和其對應的優先級並賦值給
nextFlushedRootnextFlushedExpirationTime
function findHighestPriorityRoot() { var highestPriorityWork = NoWork; var highestPriorityRoot = null; if (lastScheduledRoot !== null) { var previousScheduledRoot = lastScheduledRoot; var root = firstScheduledRoot; while (root !== null) { var remainingExpirationTime = root.expirationTime; if (remainingExpirationTime === NoWork) { // 判斷是否還有任務並移除 } else { // 找出最高的優先級root和其對應的優先級 } } } // 賦值 nextFlushedRoot = highestPriorityRoot; nextFlushedExpirationTime = highestPriorityWork; }
9.緊着, performWork會根據傳入的參數dl來判斷進行同步或者異步操做, 這裏暫不討論異步,
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime)) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true); findHighestPriorityRoot(); }
10.接着, 會進行performWorkOnRoot函數, 並傳入優先級最高的root和其對應的expirationTime以及一個true做爲參數,performWorkOnRoot函數的第三個參數isExpired主要是用來判斷是否已超過執行時間, 因爲進行的是同步操做, 因此默認超過
performWorkOnRoot函數會先將rendering狀態設爲true, 而後判斷是否異步或者超時進行操做
function performWorkOnRoot(root, expirationTime, isExpired) { // 將rendering狀態設爲true isRendering = true; // Check if this is async work or sync/expired work. if (deadline === null || isExpired) { // Flush work without yielding. // 同步 var finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. completeRoot(root, finishedWork, expirationTime); } else { root.finishedWork = null; // If this root previously suspended, clear its existing timeout, since // we're about to try rendering again. var timeoutHandle = root.timeoutHandle; if (enableSuspense && timeoutHandle !== noTimeout) { root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above cancelTimeout(timeoutHandle); } var isYieldy = false; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; if (finishedWork !== null) { // We've completed the root. Commit it. completeRoot(root, finishedWork, expirationTime); } } } else { // Flush async work.異步操做 ...... } } isRendering = false; }
11.renderRoot的產物會掛載到root的finishWork屬性上, 首先performWorkOnRoot會先判斷root的finishWork是否不爲空, 若是存在的話則直接進入commit的階段, 不然進入到renderRoot函數, 設置finishWork屬性
renderRoot有三個參數, renderRoot(root, isYieldy, isExpired), 同步狀態下isYield的值是false,
renderRoot 先將 isWorking設爲true,
renderRoot會先判斷是不是一個重新開始的root, 是的話會重置各個屬性
首先是resetStach()函數, 對原有的進行中的root任務中斷, 進行存儲
緊接着將nextRootnextRendeExpirationTime重置, 同時建立第一個nextUnitOfWork, 也就是一個工做單元
這個nextUnitOfWork也是一個workProgress, 也是root.current的alternater屬性, 而它的alternate屬性則指向了root.current, 造成了一個雙緩衝池
if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) { // 判斷是不是一個重新開始的root resetStack(); nextRoot = root; nextRenderExpirationTime = expirationTime; nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime); root.pendingCommitExpirationTime = NoWork; .... .... }
12.接着執行wookLoop(isYield)函數, 該函數經過循環執行, 遍歷每個nextUniOfWork,
function workLoop(isYieldy) { if (!isYieldy) { // Flush work without yielding while (nextUnitOfWork !== null) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } }
13.performUnitOfWork 先 獲取 參數的alaernate屬性, 賦值給current,根據註釋的意思, workInProgress是做爲一個代替品存在來操做, 而後會執行下面這個語句
next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
14.beginWork主要根據workInprogress的tag來作不一樣的處理, 並返回其child, 也就是下一個工做單元 如<div><p></p><div>, div做爲一個工做單元, 處理完後就返回工做單元p, 同時收集他們的effect
若next存在, 則返回到workLoop函數繼續循環, 若不存在, 則執行completeUnitOfWork(workInProgress)函數
completeUnitOfWork函數, 會判斷是否有sibiling, 有則直接返回賦值給next, 不然判斷父fiber是否有sibiling, 一直循環到最上層父fiber爲null, 執行的同時會把effect逐級傳給父fiber
這個時候函數執行完畢, 會返回到renderRoot函數, renderRoot函數繼續往下走
首先將isWorking = false;執行, 而後會判斷nextUnitWork是否爲空, 否的話則將root.finishWork設爲空(異步, 該任務未執行完)並結束函數
isWorking = false; if (nextUnitOfWork !== null) { onYield(root); return; }
重置nextRoot等
nextRoot = null; interruptedBy = null;
賦值finishWork
var rootWorkInProgress = root.current.alternate; onComplete(root, rootWorkInProgress, expirationTime); function onComplete(root, finishedWork, expirationTime) { root.pendingCommitExpirationTime = expirationTime; root.finishedWork = finishedWork; }
15.返回到performWorkOnRoot函數, 進入commit階段, 將rending狀態設爲false,返回到performWork函數, 繼續進入循環執行root, 直到全部root完成
重置各個狀態量, 若是還存在nextFlushedExpirationTime不爲空, 則進行scheduleCallbackWithExpirationTime函數異步操做
if (deadline !== null) { callbackExpirationTime = NoWork; callbackID = null; } // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime); } // Clean-up. deadline = null; deadlineDidExpire = false;
以上就是同步模式下的源碼分析~