摘要:函數(shù)主要執(zhí)行兩個操作,一個是判斷當(dāng)前是否還有任務(wù),如果沒有,則從鏈中移除。
系列文章
React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態(tài))
React Fiber源碼分析 第四篇(歸納總結(jié))
React Fiber是React在V16版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄~
如果有錯誤,請輕噴
1.scheduleRootUpdate這個函數(shù)主要執(zhí)行了兩個操作 1個是創(chuàng)建更新createUpdate并放到更新隊列enqueueUpdate, 1個是執(zhí)行sheculeWork函數(shù)
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函數(shù)分析, 他直接返回了一個包含了更新信息的對象
function createUpdate(expirationTime) { return { // 優(yōu)先級 expirationTime: expirationTime, // 更新類型 tag: UpdateState, // 更新的對象 payload: null, callback: null, // 指向下一個更新 next: null, // 指向下一個更新effect nextEffect: null }; }
3.接著更新payload和callback屬性, payload即為更新的對象, 然后執(zhí)行enqueuUpdate, enqueueUpdate相對比較容易理解, 不過里面有一注釋挺重要
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) { // 更新優(yōu)先級 var root = scheduleWorkToRoot(fiber, expirationTime); ...if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime < nextRenderExpirationTime) { // This is an interruption. (Used for performance tracking.) 如果這是一個打斷原有更新的任務(wù), 先把現(xiàn)有任務(wù)記錄 interruptedBy = fiber; resetStack(); } // 設(shè)置下一個操作時間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先執(zhí)行一個scheduleWorkToRoot函數(shù), 該函數(shù)主要是更新其expirationTime以及上層fiber的childrenExpirationTime
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.接著會執(zhí)行一個markPendingPriorityLevel函數(shù),這個函數(shù)主要是更新root的最高優(yōu)先級和最低優(yōu)先級(earliestPendingTime和lastestPendingTime;), 同時設(shè)置下一個執(zhí)行操作的時間nextExpirationTimeToWorkOn(即root中具有最高優(yōu)先級的fiber的expirationTime),關(guān)于這個函數(shù)的latestSuspendedTime;以后再說
最后scheduleWork會執(zhí)行requestWork
function requestWork(root, expirationTime) { addRootToSchedule(root, expirationTime); if (isRendering) { // rendering狀態(tài),直接返回 return; } if (isBatchingUpdates) { // isBatchingUpdates, 直接返回。 react的state更新是會合并的 ...return; } // TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { // 執(zhí)行同步 performSyncWork(); } else { // 異步, 暫不分析 scheduleCallbackWithExpirationTime(root, expirationTime); } }
6.requestWork 會先執(zhí)行addRootToSchedule,由函數(shù)名稱可知其作用,將root加到schedule, 即設(shè)置firstScheduledRoot, lastScheduledRoot以及他們的nextScheduleRoot屬性,說白了就是一個閉環(huán)鏈?zhǔn)浇Y(jié)構(gòu) first => next => next => last(next => first), 同時更新root的expirationTime屬性
function addRootToSchedule(root, expirationTime) { // root尚未開始過任務(wù) 將root加到schedule if (root.nextScheduledRoot === null) { ... } else { // root已經(jīng)開始執(zhí)行過任務(wù), 更新root的expirationTime var remainingExpirationTime = root.expirationTime; if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) { root.expirationTime = expirationTime; } } }
7.接著requestWork會判斷是否正在渲染中,防止重入。剩余的工作將安排在當(dāng)前渲染批次的末尾,如果正在渲染直接返回后, 因為已經(jīng)把root加上到Schedule里面了,依然會把該root執(zhí)行
同時判斷是否正在batch update, 這里留到分析setState的時候說, 最后根據(jù)異步或者同步執(zhí)行不同函數(shù), 此處執(zhí)行同步performSyncWork(),performSyncWork直接執(zhí)行performWork(Sync, null);
function performWork(minExpirationTime, dl) { deadline = dl; // 找出優(yōu)先級最高的root findHighestPriorityRoot(); if (deadline !== null) { // ...異步 } else { // 循環(huán)執(zhí)行root任務(wù) 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首先執(zhí)行findHighestPriorityRoot函數(shù)。findHighestPriorityRoot函數(shù)主要執(zhí)行兩個操作, 一個是判斷當(dāng)前root是否還有任務(wù),如果沒有, 則從firstScheuleRoot鏈中移除。 一個是找出優(yōu)先級最高的root和其對應(yīng)的優(yōu)先級并賦值給
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) { // 判斷是否還有任務(wù)并移除 } else { // 找出最高的優(yōu)先級root和其對應(yīng)的優(yōu)先級 } } } // 賦值 nextFlushedRoot = highestPriorityRoot; nextFlushedExpirationTime = highestPriorityWork; }
9.緊著, performWork會根據(jù)傳入的參數(shù)dl來判斷進(jìn)行同步或者異步操作, 這里暫不討論異步,
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime)) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true); findHighestPriorityRoot(); }
10.接著, 會進(jìn)行performWorkOnRoot函數(shù), 并傳入優(yōu)先級最高的root和其對應(yīng)的expirationTime以及一個true作為參數(shù),performWorkOnRoot函數(shù)的第三個參數(shù)isExpired主要是用來判斷是否已超過執(zhí)行時間, 由于進(jìn)行的是同步操作, 所以默認(rèn)超過
performWorkOnRoot函數(shù)會先將rendering狀態(tài)設(shè)為true, 然后判斷是否異步或者超時進(jìn)行操作
function performWorkOnRoot(root, expirationTime, isExpired) { // 將rendering狀態(tài)設(shè)為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的產(chǎn)物會掛載到root的finishWork屬性上, 首先performWorkOnRoot會先判斷root的finishWork是否不為空, 如果存在的話則直接進(jìn)入commit的階段, 否則進(jìn)入到renderRoot函數(shù), 設(shè)置finishWork屬性
renderRoot有三個參數(shù), renderRoot(root, isYieldy, isExpired), 同步狀態(tài)下isYield的值是false,
renderRoot 先將 isWorking設(shè)為true,
renderRoot會先判斷是否是一個從新開始的root, 是的話會重置各個屬性
首先是resetStach()函數(shù), 對原有的進(jìn)行中的root任務(wù)中斷, 進(jìn)行存儲
緊接著將nextRootnextRendeExpirationTime重置, 同時創(chuàng)建第一個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.接著執(zhí)行wookLoop(isYield)函數(shù), 該函數(shù)通過循環(huán)執(zhí)行, 遍歷每一個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 先 獲取 參數(shù)的alaernate屬性, 賦值給current,根據(jù)注釋的意思, workInProgress是作為一個代替品存在來操作, 然后會執(zhí)行下面這個語句
next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
14.beginWork主要根據(jù)workInprogress的tag來做不同的處理, 并返回其child, 也就是下一個工作單元 如
若next存在, 則返回到workLoop函數(shù)繼續(xù)循環(huán), 若不存在, 則執(zhí)行completeUnitOfWork(workInProgress)函數(shù)
completeUnitOfWork函數(shù), 會判斷是否有sibiling, 有則直接返回賦值給next, 否則判斷父fiber是否有sibiling, 一直循環(huán)到最上層父fiber為null, 執(zhí)行的同時會把effect逐級傳給父fiber
這個時候函數(shù)執(zhí)行完畢, 會返回到renderRoot函數(shù), renderRoot函數(shù)繼續(xù)往下走
首先將isWorking = false;執(zhí)行, 然后會判斷nextUnitWork是否為空, 否的話則將root.finishWork設(shè)為空(異步, 該任務(wù)未執(zhí)行完)并結(jié)束函數(shù)
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函數(shù), 進(jìn)入commit階段, 將rending狀態(tài)設(shè)為false,返回到performWork函數(shù), 繼續(xù)進(jìn)入循環(huán)執(zhí)行root, 直到所有root完成
重置各個狀態(tài)量, 如果還存在nextFlushedExpirationTime不為空, 則進(jìn)行scheduleCallbackWithExpirationTime函數(shù)異步操作
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;結(jié)語
以上就是同步模式下的源碼分析~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/108956.html
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇歸納總結(jié)前言是在版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄流程圖源碼分析先由編譯,調(diào)用,入?yún)?,打印出來可以看到,,分別代表著元素原生元素,回調(diào)函數(shù) 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:為什么網(wǎng)頁性能會變高要回答這個問題,需要回頭看是單線程的知識點。在分析的過程中,發(fā)現(xiàn)了的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu),回調(diào)鏈,任務(wù)鏈等,這個主要是為了增刪時性能比較高 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 Rea...
摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機(jī)制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數(shù)各司其職,輸入輸出都是可預(yù)測,一路下來很順暢。通過進(jìn)一步觀察可以發(fā)現(xiàn),預(yù)廢棄的三個生命周期函數(shù)都發(fā)生在虛擬的構(gòu)建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準(zhǔn)備前端招聘事項...
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇歸納總結(jié)前言是在版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄流程圖源碼分析調(diào)用時,會調(diào)用的方法,同時將新的作為參數(shù)傳進(jìn)會先調(diào)用獲取一個維護(hù)兩個時間一個 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:他們的應(yīng)用是比較復(fù)雜的,組件樹也是非常龐大,假設(shè)有一千個組件要渲染,每個耗費一千個就是由于是單線程的,這里都在努力的干活,一旦開始,中間就不會停。 悄悄的, React v16.7 發(fā)布了。 React v16.7: No, This Is Not The One With Hooks. showImg(https://segmentfault.com/img/bVblq9L?w=97...
閱讀 2651·2021-11-19 09:56
閱讀 921·2021-09-24 10:25
閱讀 1707·2021-09-09 09:34
閱讀 2245·2021-09-09 09:33
閱讀 1082·2019-08-30 15:54
閱讀 595·2019-08-29 18:33
閱讀 1297·2019-08-29 17:19
閱讀 533·2019-08-29 14:19