摘要:為了幫助理解,我們繼續加日志司徒正美,加群一起研究與只要收到更新對象,就會被調度程序調用。渲染器在將來的某個時刻調用。導步肯定為歡迎加繼續略也是怒長,代碼的特點是許多巨型類,巨型方法,有之遺風。
insertUpdateIntoFiber 會根據fiber的狀態創建一個或兩個列隊對象,對象是長成這樣的
//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方法,用來記錄調度器的執行狀態,如注釋所示,它現在相當于什么都沒有做
function recordScheduleUpdate() { if (enableUserTimingAPI) {//全局變量,默認為true if (isCommitting) {//全局變量,默認為false, 沒有進入分支 hasScheduledUpdateInCurrentCommit = true; } //全局變量,默認為null,沒有沒有進入分支 if (currentPhase !== null && currentPhase !== "componentWillMount" && currentPhase !== "componentWillReceiveProps") { hasScheduledUpdateInCurrentPhase = true; } } }
scheduleWorkImpl的一些分支非常復雜,我們打一些斷點
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"]; } }
輸出如下
requestWork也很難理解,里面太多全局變量,覺得不是前端的人搞的。為了幫助理解,我們繼續加日志
//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意為努力工作(正式上班)
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,因此進入同步。導步肯定為false
// 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),后面都是錯誤邊界的
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) { //略 } }
總結一下到現在的所有流程:
好了,今天就這么多,想必大家都累了,下一篇繼續
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107182.html
摘要:司徒正美,加群一起研究與用于調整渲染順序,高優先級的組件先執行這只是一部分更新邏輯,簡直沒完沒了,下次繼續添上流程圖,回憶一下本文學到的東西 React16真是一天一改,如果現在不看,以后也很難看懂了。 在React16中,雖然也是通過JSX編譯得到一個虛擬DOM對象,但對這些虛擬DOM對象的再加工則是經過翻天覆地的變化。我們需要追根溯底,看它是怎么一步步轉換過來的。我們先不看什么組件...
摘要:引言于發布版本,時至今日已更新到,且引入了大量的令人振奮的新特性,本文章將帶領大家根據更新的時間脈絡了解的新特性。其作用是根據傳遞的來更新。新增等指針事件。 1 引言 于 2017.09.26 Facebook 發布 React v16.0 版本,時至今日已更新到 React v16.6,且引入了大量的令人振奮的新特性,本文章將帶領大家根據 React 更新的時間脈絡了解 React1...
摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數各司其職,輸入輸出都是可預測,一路下來很順暢。通過進一步觀察可以發現,預廢棄的三個生命周期函數都發生在虛擬的構建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準備前端招聘事項...
摘要:架構理解引用原文是核心算法正在進行的重新實現。構建的過程就是的過程,通過來調度執行一組任務,每完成一個任務后回來看看有沒有插隊的更緊急的,把時間控制權交還給主線程,直到下一次回調再繼續構建。 React Fiber 架構理解 引用原文:React Fiber ArchitectureReact Fiber is an ongoing reimplementation of Reacts...
閱讀 3156·2021-11-22 09:34
閱讀 2796·2021-09-22 15:28
閱讀 816·2021-09-10 10:51
閱讀 1853·2019-08-30 14:22
閱讀 2273·2019-08-30 14:17
閱讀 2734·2019-08-30 11:01
閱讀 2295·2019-08-29 17:19
閱讀 3653·2019-08-29 13:17