摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇?dú)w納總結(jié)前言是在版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄流程圖源碼分析調(diào)用時(shí),會(huì)調(diào)用的方法,同時(shí)將新的作為參數(shù)傳進(jìn)會(huì)先調(diào)用獲取一個(gè)維護(hù)兩個(gè)時(shí)間一個(gè)
系列文章
React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態(tài))
React Fiber源碼分析 第四篇(歸納總結(jié))
React Fiber是React在V16版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄~
流程圖 源碼分析1.調(diào)用setState時(shí), 會(huì)調(diào)用classComponentUpdater的enqueueSetState方法, 同時(shí)將新的state作為payload參數(shù)傳進(jìn)enqueueSetState會(huì)先調(diào)用requestCurrentTime獲取一個(gè)currentTime
function requestCurrentTime() { // 維護(hù)兩個(gè)時(shí)間 一個(gè)renderingTime 一個(gè)currentSechedulerTime // rederingTime 可以隨時(shí)更新 currentSechedulerTime只有在沒有新任務(wù)的時(shí)候才更新 if (isRendering) { return currentSchedulerTime; } findHighestPriorityRoot(); if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) { recomputeCurrentRendererTime(); currentSchedulerTime = currentRendererTime; return currentSchedulerTime; } return currentSheculerTime
2.通過(guò)獲取到的currentTime, 調(diào)用computeExpirationForFiber,計(jì)算該fiber的優(yōu)先級(jí)
if (fiber.mode & AsyncMode) { if (isBatchingInteractiveUpdates) { // This is an interactive update expirationTime = computeInteractiveExpiration(currentTime); } else { // This is an async update expirationTime = computeAsyncExpiration(currentTime); } ... }
3.這個(gè)函數(shù)其他點(diǎn)比較簡(jiǎn)單, 里面主要有下面 這個(gè)判斷要說(shuō)明一下, 如果是屬于異步更新的話,會(huì)根據(jù)是 交互引起的更新 還是其他更新 來(lái)調(diào)用不同的函數(shù)computeInteractiveExpiration和computeAsyncExpiration,
可以看到這兩個(gè)函數(shù)最后返回的都是computeExpirationBucket函數(shù)的結(jié)果, 只是入?yún)⒉煌?computeInteractiveExpiration的參數(shù)是500, 100, computeAsyncExpiration的參數(shù)是5000, 250, 然后看computeExpirationBucket函數(shù)可以看到, 第二個(gè)參數(shù)(500和5000)越大,則返回的expirationTime越大, 也就是說(shuō) computeInteractiveExpiration的更新優(yōu)先級(jí)高于computeAsyncExpiration, 則交互的優(yōu)先級(jí)高于其他
獲得優(yōu)先級(jí)后則和同步更新一樣, 創(chuàng)建update并放進(jìn)隊(duì)列, 然后調(diào)用sheuduleWork
var classComponentUpdater = { isMounted: isMounted, enqueueSetState: function (inst, payload, callback) { var fiber = get(inst); // 獲得優(yōu)先級(jí) var currentTime = requestCurrentTime(); var expirationTime = computeExpirationForFiber(currentTime, fiber); // 創(chuàng)建更新 var update = createUpdate(expirationTime); update.payload = payload; if (callback !== undefined && callback !== null) { update.callback = callback; } enqueueUpdate(fiber, update); scheduleWork(fiber, expirationTime); },
4.接下來(lái)的步驟和同步一樣, 直到同步調(diào)用的是performSyncWork函數(shù), 而異步調(diào)用的是scheduleCallbackWithExpirationTime函數(shù)
scheduleCallbackWithExpirationTime函數(shù)首先判斷是否存在callback正在進(jìn)行中, 判斷現(xiàn)有expirationTime和其優(yōu)先級(jí),若優(yōu)先級(jí)比較低則直接返回, 否則設(shè)置現(xiàn)在的fiber任務(wù)為新的callback,并把原來(lái)的回調(diào)從列表中移除
function scheduleCallbackWithExpirationTime(root, expirationTime) { if (callbackExpirationTime !== NoWork) { // 判斷優(yōu)先級(jí) if (expirationTime > callbackExpirationTime) { // Existing callback has sufficient timeout. Exit. return; } else { if (callbackID !== null) { // 取消, 從回調(diào)列表中刪除 schedule.unstable_cancelScheduledWork(callbackID); } } // The request callback timer is already running. Don"t start a new one. } // 設(shè)置新的callback和callbackExiporationTime callbackExpirationTime = expirationTime; var currentMs = schedule.unstable_now() - originalStartTimeMs; var expirationTimeMs = expirationTimeToMs(expirationTime); // 計(jì)算是否超時(shí) var timeout = expirationTimeMs - currentMs; callbackID = schedule.unstable_scheduleWork(performAsyncWork, { timeout: timeout }); }
5.接下來(lái)調(diào)用schedule.unstable_scheduleWork(performAsyncWork, { timeout: timeout })函數(shù), 并生成一個(gè)節(jié)點(diǎn), 存儲(chǔ)回調(diào)函數(shù)和超時(shí)時(shí)間,插入到回調(diào)列表, 并根據(jù)超時(shí)排序, 調(diào)用ensureHostCallBackIsScheduled函數(shù),最后返回該節(jié)點(diǎn)
function unstable_scheduleWork(callback, options) { var currentTime = exports.unstable_now(); var timesOutAt; // 獲取超時(shí)時(shí)間 if (options !== undefined && options !== null && options.timeout !== null && options.timeout !== undefined) { // Check for an explicit timeout timesOutAt = currentTime + options.timeout; } else { // Compute an absolute timeout using the default constant. timesOutAt = currentTime + DEFERRED_TIMEOUT; } // 生成一個(gè)節(jié)點(diǎn), 存儲(chǔ)回調(diào)函數(shù)和超時(shí)時(shí)間 var newNode = { callback: callback, timesOutAt: timesOutAt, next: null, previous: null }; // 插入到回調(diào)列表, 并根據(jù)超時(shí)排序, 最后返回該節(jié)點(diǎn) if (firstCallbackNode === null) { // This is the first callback in the list. firstCallbackNode = newNode.next = newNode.previous = newNode; ensureHostCallbackIsScheduled(firstCallbackNode); } else { ...var previous = next.previous; previous.next = next.previous = newNode; newNode.next = next; newNode.previous = previous; } return newNode; }
6.ensureHostCallBackIsScheduled函數(shù)如名, 相對(duì)比較簡(jiǎn)單
function ensureHostCallbackIsScheduled() { if (isPerformingWork) { // Don"t schedule work yet; wait until the next time we yield. return; } // Schedule the host callback using the earliest timeout in the list. var timesOutAt = firstCallbackNode.timesOutAt; if (!isHostCallbackScheduled) { isHostCallbackScheduled = true; } else { // Cancel the existing host callback. cancelCallback(); } requestCallback(flushWork, timesOutAt); }
7.往下看requestCallback, 這里說(shuō)的如果已經(jīng)在執(zhí)行任務(wù)的話, 就必須有一個(gè)錯(cuò)誤被拋出(拋出的錯(cuò)誤是啥??),同時(shí)不要等待下一幀, 盡快開始新事件
如果如果當(dāng)前沒有調(diào)度幀回調(diào)函數(shù),我們需要進(jìn)行一個(gè)調(diào)度幀回調(diào)函數(shù), 并設(shè)置isAnimationFrameScheduled為true,
接著執(zhí)行requestAnimationFrameWithTimeout;函數(shù)
requestCallback = function (callback, absoluteTimeout) { scheduledCallback = callback; timeoutTime = absoluteTimeout; if (isPerformingIdleWork) { // 如果已經(jīng)在執(zhí)行任務(wù)的話, 就必須有一個(gè)錯(cuò)誤被拋出(拋出的錯(cuò)誤是啥??),同時(shí)不要等待下一幀, 盡快開始新事件 window.postMessage(messageKey, "*"); } else if (!isAnimationFrameScheduled) { isAnimationFrameScheduled = true; requestAnimationFrameWithTimeout(animationTick); } };
8.requestAnimationFrameWithTimeout函數(shù)就是執(zhí)行一個(gè)異步操作, 執(zhí)行完畢后, 假設(shè)此時(shí)又有N個(gè)回調(diào)任務(wù)進(jìn)入, 同時(shí)原來(lái)的回調(diào)還沒有進(jìn)行, 則回到scheduleCallbackWithExpirationTime函數(shù)上,
分為兩個(gè)分支:
假設(shè)優(yōu)先級(jí)低于目前的回調(diào)任務(wù), 則直接返回(已經(jīng)把root加到root隊(duì)列中)
優(yōu)先級(jí)高于目前的回調(diào)任務(wù), 將目前的回調(diào)任務(wù)從列表中移除, 并將callBackID設(shè)為傳入的回調(diào), 接下來(lái)的路線與上面一致, 假設(shè)該傳入的回調(diào)超時(shí)最早, 則會(huì)進(jìn)入到cancelCallback函數(shù),重置各變量, 并進(jìn)入到requestCallback函數(shù), 此時(shí)除了賦值操作,沒有其他動(dòng)作
到了這時(shí)候, 已經(jīng)把新的回調(diào)替換正在進(jìn)行的回調(diào)到回調(diào)列表。
函數(shù)正常執(zhí)行, 調(diào)用callback, 即animationTick函數(shù)
cancelCallback = function () { scheduledCallback = null; isIdleScheduled = false; timeoutTime = -1; };
var ANIMATION_FRAME_TIMEOUT = 100; var rAFID; var rAFTimeoutID; var requestAnimationFrameWithTimeout = function (callback) { // schedule rAF and also a setTimeout rAFID = localRequestAnimationFrame(function (timestamp) { // cancel the setTimeout localClearTimeout(rAFTimeoutID); callback(timestamp); }); rAFTimeoutID = localSetTimeout(function () { // cancel the requestAnimationFrame localCancelAnimationFrame(rAFID); callback(exports.unstable_now()); }, ANIMATION_FRAME_TIMEOUT); };
9.animationTick一個(gè)是把isAnimationFrameScheduled狀態(tài)設(shè)為false, 即不在調(diào)度幀回調(diào)的狀態(tài), 同時(shí)計(jì)算幀到期時(shí)間frameDeadline , 判斷是否在幀回調(diào)的狀態(tài), 否的話調(diào)用window.postMessage ,并設(shè)置isIdleScheduled狀態(tài)為true
假設(shè)此時(shí), 有N個(gè)回調(diào)進(jìn)入, 分為兩個(gè)情況:
1.假設(shè)優(yōu)先級(jí)低于目前的回調(diào)任務(wù), 則直接返回(已經(jīng)把root加到root隊(duì)列中)
2.優(yōu)先級(jí)高于目前的回調(diào)任務(wù), 將目前的回調(diào)任務(wù)從列表中移除, 并將callBackID設(shè)為傳入的回調(diào), 接下來(lái)的路線與上面一致,一直到animationTick函數(shù),因?yàn)? postMessage比setTImeout更快執(zhí)行,所以此時(shí)isIdleScheduled為false,和之前一樣正常執(zhí)行。
var animationTick = function (rafTime) { isAnimationFrameScheduled = false; ... ... // 每幀到期時(shí)間為33ms frameDeadline = rafTime + activeFrameTime; if (!isIdleScheduled) { isIdleScheduled = true; window.postMessage(messageKey, "*"); } };
10.postMessage會(huì)執(zhí)行idleTick , 首先把isIdleScheduleddidTimeout置為false,
先判斷幀到期時(shí)間和超時(shí)時(shí)間是否小于當(dāng)前時(shí)間, 如果是的話, 則置didTimeout為true,
如果幀到期, 但超時(shí)時(shí)間小于當(dāng)前時(shí)間, 則置isAnimationFrameScheduled 為false, 并調(diào)用requestAnimationFrameWithTimeout, 即進(jìn)入下一幀
如果幀未到期, 則調(diào)用callbak函數(shù), 并把isPerformingIdleWork置為true
idleTick 會(huì)先執(zhí)行callback, 完成后才將isPerformingIdleWork 置為false, 執(zhí)行callback的時(shí)候會(huì)傳入didTimeout作為參數(shù), callback為flushWork
var idleTick = function (event) { ... isIdleScheduled = false; var currentTime = exports.unstable_now(); var didTimeout = false; if (frameDeadline - currentTime <= 0) { // 幀過(guò)期 if (timeoutTime !== -1 && timeoutTime <= currentTime) { // 回調(diào)超時(shí) didTimeout = true; } else { // No timeout. if (!isAnimationFrameScheduled) { // 到下一幀繼續(xù)任務(wù) isAnimationFrameScheduled = true; requestAnimationFrameWithTimeout(animationTick); } // Exit without invoking the callback. return; } } timeoutTime = -1; var callback = scheduledCallback; scheduledCallback = null; if (callback !== null) { isPerformingIdleWork = true; try { callback(didTimeout); } finally { isPerformingIdleWork = false; } } };
11.flushwork首先把isPerformingWork置為true, 然后把didTimeout賦值給deallinObject對(duì)象, 接下來(lái)進(jìn)行判斷
如果已經(jīng)過(guò)了幀的結(jié)束期, 則判斷鏈表中有哪個(gè)節(jié)點(diǎn)已超時(shí), 并循環(huán)調(diào)用flushFirstCallback函數(shù)解決超時(shí)節(jié)點(diǎn),
如果還沒有過(guò)幀的結(jié)束期, 則調(diào)用flushFirstCallback函數(shù)處理鏈表中的第一個(gè)節(jié)點(diǎn), 循環(huán)處理一直到該幀結(jié)束
最后, flushwork函數(shù)會(huì)將isPerformingWork置為false, 并判斷是否還有任務(wù) 有則執(zhí)行ensureHostCallbackIsScheduled函數(shù)
function flushWork(didTimeout) { isPerformingWork = true; deadlineObject.didTimeout = didTimeout; try { if (didTimeout) { while (firstCallbackNode !== null) { var currentTime = exports.unstable_now(); if (firstCallbackNode.timesOutAt <= currentTime) { do { flushFirstCallback(); } while (firstCallbackNode !== null && firstCallbackNode.timesOutAt <= currentTime); continue; } break; } } else { // Keep flushing callbacks until we run out of time in the frame. if (firstCallbackNode !== null) { do { flushFirstCallback(); } while (firstCallbackNode !== null && getFrameDeadline() - exports.unstable_now() > 0); } } } finally { isPerformingWork = false; if (firstCallbackNode !== null) { // There"s still work remaining. Request another callback. ensureHostCallbackIsScheduled(firstCallbackNode); } else { isHostCallbackScheduled = false; } } }
12.繼續(xù)往下看, 則是flushFirstCallback函數(shù),先把該節(jié)點(diǎn)從鏈表中清掉, 然后調(diào)用callback函數(shù), 并帶入deadlineObject作為參數(shù)
function flushFirstCallback(node) { var flushedNode = firstCallbackNode; //從鏈表中清理掉該節(jié)點(diǎn), 這樣哪怕出錯(cuò)了, 也能保留原鏈表狀態(tài) var next = firstCallbackNode.next; if (firstCallbackNode === next) { // This is the last callback in the list. firstCallbackNode = null; next = null; } else { var previous = firstCallbackNode.previous; firstCallbackNode = previous.next = next; next.previous = previous; } flushedNode.next = flushedNode.previous = null; // Now it"s safe to call the callback. var callback = flushedNode.callback; callback(deadlineObject); }
13.接下來(lái)的就是performAsyncWork函數(shù),如果didTimeout為true, 則表明至少有一個(gè)更新已過(guò)期, 迭代所有root任務(wù), 把已過(guò)期的root的nextExpirationTimeToWorkOn重置為當(dāng)前時(shí)間currentTime.
然后調(diào)用performWork函數(shù)
function performAsyncWork(dl) { if (dl.didTimeout) { // 刷新所有root的nextEpirationTimeToWorkOn if (firstScheduledRoot !== null) { recomputeCurrentRendererTime(); var root = firstScheduledRoot; do { didExpireAtExpirationTime(root, currentRendererTime); // The root schedule is circular, so this is never null. root = root.nextScheduledRoot; } while (root !== firstScheduledRoot); } } performWork(NoWork, dl); }
14.performWork函數(shù)在之前已經(jīng)分析過(guò)了, 這里主要看存在deadline時(shí)的操作, 在幀未到期 或者 當(dāng)前渲染時(shí)間大于等于nextFlushedExpirationTime時(shí)才執(zhí)行 performWorkOnRoot, 并將currentRendererTime >= nextFlushedExpirationTime作為第三個(gè)參數(shù)傳入, 一直循環(huán)處理任務(wù),
最后清除callbackExpirationTime, callBackId, 同時(shí), 如果還有任務(wù)的話, 則繼續(xù)調(diào)用scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);函數(shù)進(jìn)入到回調(diào)
function performWork(minExpirationTime, dl) { deadline = dl; // Keep working on roots until there"s no more work, or until we reach // the deadline. findHighestPriorityRoot(); if (deadline !== null) { recomputeCurrentRendererTime(); currentSchedulerTime = currentRendererTime;while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime) && (!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, currentRendererTime >= nextFlushedExpirationTime); findHighestPriorityRoot(); recomputeCurrentRendererTime(); currentSchedulerTime = currentRendererTime; } } 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; finishRendering(); }
15.接下來(lái)看異步狀態(tài)下的performWorkOnRoot函數(shù)。基本操作和同步一樣, 在進(jìn)入到renderRoot(root, _isYieldy, isExpired);函數(shù)時(shí), 會(huì)根據(jù)是否已超時(shí)將isYieldy置為true或者false, 異步狀態(tài)下未超時(shí)為false,
renderRoot和同步一樣, 最后執(zhí)行workLoop(isYieldy)
workLoop在未過(guò)期的情況下, 會(huì)執(zhí)行shouldYield()函數(shù)來(lái)判斷是否執(zhí)行nextUnitOfWork, 和同步一樣, 這里只需要關(guān)注shouldYied函數(shù)
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); } } }
16.shouldYield函數(shù), 如果deadlineDidExpire為true, 即幀已到期, 直接返回true,
如果deadline不存在, 并且?guī)吹狡冢?則返回false, 可以執(zhí)行單元
否則將deadlineDidExpire置為true
function shouldYield() { if (deadlineDidExpire) { return true; } if (deadline === null || deadline.timeRemaining() > timeHeuristicForUnitOfWork) { // Disregard deadline.didTimeout. Only expired work should be flushed // during a timeout. This path is only hit for non-expired work. return false; } deadlineDidExpire = true; return true; }總結(jié)
源碼分析到這里就結(jié)束啦,下一篇做一個(gè)總結(jié),不然就是流水賬一樣的,容易忘記
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/108977.html
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇?dú)w納總結(jié)前言是在版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄流程圖源碼分析先由編譯,調(diào)用,入?yún)椋蛴〕鰜?lái)可以看到,,分別代表著元素原生元素,回調(diào)函數(shù) 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:函數(shù)主要執(zhí)行兩個(gè)操作,一個(gè)是判斷當(dāng)前是否還有任務(wù),如果沒有,則從鏈中移除。 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 React Fiber是React在V16版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記...
摘要:為什么網(wǎng)頁(yè)性能會(huì)變高要回答這個(gè)問題,需要回頭看是單線程的知識(shí)點(diǎn)。在分析的過(guò)程中,發(fā)現(xiàn)了的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu),回調(diào)鏈,任務(wù)鏈等,這個(gè)主要是為了增刪時(shí)性能比較高 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 Rea...
摘要:因?yàn)榘姹緦⒄嬲龔U棄這三生命周期到目前為止,的渲染機(jī)制遵循同步渲染首次渲染,更新時(shí)更新時(shí)卸載時(shí)期間每個(gè)周期函數(shù)各司其職,輸入輸出都是可預(yù)測(cè),一路下來(lái)很順暢。通過(guò)進(jìn)一步觀察可以發(fā)現(xiàn),預(yù)廢棄的三個(gè)生命周期函數(shù)都發(fā)生在虛擬的構(gòu)建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時(shí)間準(zhǔn)備前端招聘事項(xiàng)...
摘要:在上面我們已經(jīng)知道瀏覽器是一幀一幀執(zhí)行的,在兩個(gè)執(zhí)行幀之間,主線程通常會(huì)有一小段空閑時(shí)間,可以在這個(gè)空閑期調(diào)用空閑期回調(diào),執(zhí)行一些任務(wù)。另外由于這些堆棧是可以自己控制的,所以可以加入并發(fā)或者錯(cuò)誤邊界等功能。 文章首發(fā)于個(gè)人博客 前言 2016 年都已經(jīng)透露出來(lái)的概念,這都 9102 年了,我才開始寫 Fiber 的文章,表示慚愧呀。不過(guò)現(xiàn)在好的是關(guān)于 Fiber 的資料已經(jīng)很豐富了,...
閱讀 2123·2019-08-29 16:53
閱讀 2698·2019-08-29 16:07
閱讀 2041·2019-08-29 13:13
閱讀 3266·2019-08-26 13:57
閱讀 1331·2019-08-26 13:31
閱讀 2432·2019-08-26 13:22
閱讀 1220·2019-08-26 11:43
閱讀 2084·2019-08-23 17:14