insertUpdateIntoFiber 會根據fiber的狀態建立一個或兩個列隊對象,對象是長成這樣的javascript
//by 司徒正美, 加羣:370262116 一塊兒研究React與anujs // https://github.com/RubyLouvre/anu 歡迎加star function createUpdateQueue(baseState) {//咱們如今是丟了一個null作傳參 var queue = { baseState: baseState, expirationTime: NoWork,//NoWork會被當即執行 first: null, last: null, callbackList: null, hasForceUpdate: false, isInitialized: false }; return queue; }
scheduleWork是一個奇怪的方法,只是添加一下參數前端
function scheduleWork(fiber, expirationTime) { return scheduleWorkImpl(fiber, expirationTime, false); }
scheduleWorkImpl的最開頭有一個recordScheduleUpdate方法,用來記錄調度器的執行狀態,如註釋所示,它如今至關於什麼都沒有作java
function recordScheduleUpdate() { if (enableUserTimingAPI) {//全局變量,默認爲true if (isCommitting) {//全局變量,默認爲false, 沒有進入分支 hasScheduledUpdateInCurrentCommit = true; } //全局變量,默認爲null,沒有沒有進入分支 if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') { hasScheduledUpdateInCurrentPhase = true; } } }
scheduleWorkImpl的一些分支很是複雜,咱們打一些斷點node
function computeExpirationForFiber(fiber) { var expirationTime = void 0; if (expirationContext !== NoWork) { // An explicit expiration context was set; expirationTime = expirationContext; } else if (isWorking) { if (isCommitting) { // Updates that occur during the commit phase should have sync priority // by default. expirationTime = Sync; } else { // Updates during the render phase should expire at the same time as // the work that is being rendered. expirationTime = nextRenderExpirationTime; } } else { // No explicit expiration context was set, and we're not currently // performing work. Calculate a new expiration time. if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) { // This is a sync update console.log("expirationTime", Sync) expirationTime = Sync;//命中這裏 } else { // This is an async update expirationTime = computeAsyncExpiration(); } } return expirationTime; } function checkRootNeedsClearing(root, fiber, expirationTime) { if (!isWorking && root === nextRoot && expirationTime < nextRenderExpirationTime) { console.log("checkRootNeedsClearing對nextRoot,nextUnitOfWork,nextRenderExpirationTime進行置空") // Restart the root from the top. if (nextUnitOfWork !== null) { // This is an interruption. (Used for performance tracking.) interruptedBy = fiber; } nextRoot = null; nextUnitOfWork = null; nextRenderExpirationTime = NoWork; }else{ console.log("checkRootNeedsClearing就是想醬油") } } function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) { recordScheduleUpdate();//如今什麼也沒作 var node = fiber; while (node !== null) { // Walk the parent path to the root and update each node's // expiration time. if (node.expirationTime === NoWork || node.expirationTime > expirationTime) { node.expirationTime = expirationTime;//因爲默認就是NoWork,所以會被重寫 Sync } if (node.alternate !== null) {//這裏進不去 if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) { node.alternate.expirationTime = expirationTime; } } if (node['return'] === null) { if (node.tag === HostRoot) {//進入這裏 var root = node.stateNode; checkRootNeedsClearing(root, fiber, expirationTime); console.log("requestWork",root, expirationTime) requestWork(root, expirationTime); checkRootNeedsClearing(root, fiber, expirationTime); } else { return; } } node = node['return']; } }
輸出以下react
requestWork也很難理解,裏面太多全局變量,以爲不是前端的人搞的。爲了幫助理解,咱們繼續加日誌git
//by 司徒正美, 加羣:370262116 一塊兒研究React與anujs // requestWork is called by the scheduler whenever a root receives an update. // It's up to the renderer to call renderRoot at some point in the future. /* 只要root收到更新(update對象),requestWork就會被調度程序調用。 渲染器在未來的某個時刻調用renderRoot。 */ function requestWork(root, expirationTime) { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { invariant_1(false, 'Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.'); } // Add the root to the schedule. // Check if this root is already part of the schedule. if (root.nextScheduledRoot === null) { // This root is not already scheduled. Add it. console.log("設置remainingExpirationTime",expirationTime) root.remainingExpirationTime = expirationTime; if (lastScheduledRoot === null) { console.log("設置firstScheduledRoot, lastScheduledRoot") firstScheduledRoot = lastScheduledRoot = root; root.nextScheduledRoot = root; } else { lastScheduledRoot.nextScheduledRoot = root; lastScheduledRoot = root; lastScheduledRoot.nextScheduledRoot = firstScheduledRoot; } } else { // This root is already scheduled, but its priority may have increased. var remainingExpirationTime = root.remainingExpirationTime; if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) { // Update the priority. root.remainingExpirationTime = expirationTime; } } if (isRendering) { // Prevent reentrancy. Remaining work will be scheduled at the end of // the currently rendering batch. return; } if (isBatchingUpdates) { // Flush work at the end of the batch. if (isUnbatchingUpdates) { // ...unless we're inside unbatchedUpdates, in which case we should // flush it now. nextFlushedRoot = root; nextFlushedExpirationTime = Sync; console.log("performWorkOnRoot") performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime); } return; } // TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { console.log("進入performWork") performWork(Sync, null); } else { scheduleCallbackWithExpiration(expirationTime); } }
從日誌輸出來看,requestWork只是修改了兩個全局變量,而後進入performWork。這三個內部方法起名頗有意思。scheduleWork意爲打算工做
,requestWork意爲申請工做
,performWork意爲努力工做(正式上班)
github
function performWork(minExpirationTime, dl) { deadline = dl; // Keep working on roots until there's no more work, or until the we reach // the deadline. //這裏會將root設置爲highestPriorityRoot findHighestPriorityRoot(); if (enableUserTimingAPI && deadline !== null) { var didExpire = nextFlushedExpirationTime < recalculateCurrentTime(); console.log(didExpire) stopRequestCallbackTimer(didExpire); } while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || nextFlushedExpirationTime <= minExpirationTime) && !deadlineDidExpire) { console.log("performWorkOnRoot") performWorkOnRoot(highestPriorityRoot, nextFlushedExpirationTime); // Find the next highest priority work. findHighestPriorityRoot(); } // We're done flushing work. Either we ran out of time in this callback, // or there's no more work left with sufficient priority. // If we're inside a callback, set this to false since we just completed it. if (deadline !== null) { callbackExpirationTime = NoWork; callbackID = -1; } // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { console.log("scheduleCallbackWithExpiration") scheduleCallbackWithExpiration(nextFlushedExpirationTime); } // Clean-up. deadline = null; deadlineDidExpire = false; nestedUpdateCount = 0; if (hasUnhandledError) { //若是有沒處理的錯誤則throw var _error4 = unhandledError; unhandledError = null; hasUnhandledError = false; throw _error4; } }
咱們終於進入performWorkOnRoot,performWorkOnRoot的做用是區分同步渲染仍是異步渲染,expirationTime等於1,所以進入同步。導步確定爲falseapp
// https://github.com/RubyLouvre/anu 歡迎加star function performWorkOnRoot(root, expirationTime) { isRendering = true; // Check if this is async work or sync/expired work. // TODO: Pass current time as argument to renderRoot, commitRoot if (expirationTime <= recalculateCurrentTime()) { // Flush sync work. var finishedWork = root.finishedWork; console.log("Flush sync work.", finishedWork) if (finishedWork !== null) { // This root is already complete. We can commit it. root.finishedWork = null; console.log("commitRoot") root.remainingExpirationTime = commitRoot(finishedWork); } else { root.finishedWork = null; console.log("renderRoot") finishedWork = renderRoot(root, expirationTime); if (finishedWork !== null) { console.log("繼續commitRoot") // We've completed the root. Commit it. root.remainingExpirationTime = commitRoot(finishedWork); } } } else { console.log("Flush async work.") // Flush async work. // ...略 } isRendering = false; }
renderRoot也是怒長,React16代碼的特色是許多巨型類,巨型方法,有JAVA之遺風。renderRoot只有前面幾行是可能處理虛擬DOM(或叫fiber),後面都是錯誤邊界的less
function renderRoot(root, expirationTime) { isWorking = true; // We're about to mutate the work-in-progress tree. If the root was pending // commit, it no longer is: we'll need to complete it again. root.isReadyForCommit = false; // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if (root !== nextRoot || expirationTime !== nextRenderExpirationTime || nextUnitOfWork === null) { // Reset the stack and start working from the root. resetContextStack(); nextRoot = root; nextRenderExpirationTime = expirationTime; //多是用來工做的代碼 console.log("createWorkInProgress") nextUnitOfWork = createWorkInProgress(nextRoot.current, null, expirationTime); } //多是用來工做的代碼 console.log("startWorkLoopTimer") startWorkLoopTimer(nextUnitOfWork); // 處理錯誤邊界 var didError = false; var error = null; invokeGuardedCallback$1(null, workLoop, null, expirationTime); // An error was thrown during the render phase. while (didError) { console.log("componentDidCatch的相關實現") if (didFatal) { // This was a fatal error. Don't attempt to recover from it. firstUncaughtError = error; break; } var failedWork = nextUnitOfWork; if (failedWork === null) { // An error was thrown but there's no current unit of work. This can // happen during the commit phase if there's a bug in the renderer. didFatal = true; continue; } // 處理錯誤邊界 var boundary = captureError(failedWork, error); !(boundary !== null) ? invariant_1(false, 'Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue.') : void 0; if (didFatal) { // The error we just captured was a fatal error. This happens // when the error propagates to the root more than once. continue; } // 處理錯誤邊界 didError = false; error = null; // We're finished working. Exit the error loop. break; } // 處理錯誤邊界 var uncaughtError = firstUncaughtError; // We're done performing work. Time to clean up. stopWorkLoopTimer(interruptedBy); interruptedBy = null; isWorking = false; didFatal = false; firstUncaughtError = null; // 處理錯誤邊界 if (uncaughtError !== null) { onUncaughtError(uncaughtError); } return root.isReadyForCommit ? root.current.alternate : null; } function resetContextStack() { // Reset the stack reset$1(); // Reset the cursors resetContext(); resetHostContainer(); } function reset$1() { console.log("reset",index) while (index > -1) { valueStack[index] = null; { fiberStack[index] = null; } index--; } } function resetContext() { consoel.log("resetContext") previousContext = emptyObject_1; contextStackCursor.current = emptyObject_1; didPerformWorkStackCursor.current = false; } function resetHostContainer() { console.log("resetHostContainer",contextStackCursor, rootInstanceStackCursor, NO_CONTEXT ) contextStackCursor.current = NO_CONTEXT; rootInstanceStackCursor.current = NO_CONTEXT; }
createWorkInProgress就是將根組件的fiber對象再複製一份,變成其alternate屬性。所以 將虛擬DOM轉換爲真實DOM的重任就交給invokeGuardedCallback異步
var invokeGuardedCallback = function (name, func, context, a, b, c, d, e, f) { ReactErrorUtils._hasCaughtError = false; ReactErrorUtils._caughtError = null; var funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { ReactErrorUtils._caughtError = error; ReactErrorUtils._hasCaughtError = true; } //這下面還有怒長(100-150L )的關於錯誤邊界的處理,略過 };
func爲workLoop
//by 司徒正美, 加羣:370262116 一塊兒研究React與anujs function workLoop(expirationTime) { if (capturedErrors !== null) { // If there are unhandled errors, switch to the slow work loop. // TODO: How to avoid this check in the fast path? Maybe the renderer // could keep track of which roots have unhandled errors and call a // forked version of renderRoot. slowWorkLoopThatChecksForFailedWork(expirationTime); return; } if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) { return; } if (nextRenderExpirationTime <= mostRecentCurrentTime) { // Flush all expired work. while (nextUnitOfWork !== null) { console.log("performUnitOfWork",nextUnitOfWork) nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } }
咱們終於看到工做的代碼了。 這個nextUnitOfWork 是renderRoot生成的
performUnitOfWork與beginWork的代碼,裏面會根據fiber的tag進入各類操做
//by 司徒正美, 加羣:370262116 一塊兒研究React與anujs // https://github.com/RubyLouvre/anu 歡迎加star function performUnitOfWork(workInProgress) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here // means that we don't need an additional field on the work in // progress. var current = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } console.log("beginWork") var next = beginWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { console.log("next") // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; } function beginWork(current, workInProgress, renderExpirationTime) { if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } switch (workInProgress.tag) { case IndeterminateComponent: return mountIndeterminateComponent(current, workInProgress, renderExpirationTime); case FunctionalComponent: return updateFunctionalComponent(current, workInProgress); case ClassComponent: return updateClassComponent(current, workInProgress, renderExpirationTime); case HostRoot: return updateHostRoot(current, workInProgress, renderExpirationTime); case HostComponent: return updateHostComponent(current, workInProgress, renderExpirationTime); case HostText: return updateHostText(current, workInProgress); case CallHandlerPhase: // This is a restart. Reset the tag to the initial phase. workInProgress.tag = CallComponent; // Intentionally fall through since this is now the same. case CallComponent: return updateCallComponent(current, workInProgress, renderExpirationTime); case ReturnComponent: // A return component is just a placeholder, we can just run through the // next one immediately. return null; case HostPortal: return updatePortalComponent(current, workInProgress, renderExpirationTime); case Fragment: return updateFragment(current, workInProgress); default: invariant_1(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); } }
咱們再調查一下workInProgress.tag
是什麼
https://github.com/facebook/r...
這裏有所有fiber節點的類型描述,咱們建立一個對象
// https://github.com/RubyLouvre/anu 歡迎加star var mapBeginWork = { 3: "HostRoot 根組件", 0: "IndeterminateComponent 只知道type爲函數", 2: "ClassComponent 普通類組件" , 5: "HostComponent 元素節點", 6: "HostText 文本節點" } function beginWork(current, workInProgress, renderExpirationTime) { if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } console.log(workInProgress.tag, mapBeginWork[workInProgress.tag]) switch (workInProgress.tag) { //略 } }
總結一下到如今的全部流程:
好了,今天就這麼多,想必你們都累了,下一篇繼續