本文主要和大家分享React16.2的fiber架构详解,希望能帮助到大家。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 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 <p>从日志输出来看,requestWork只是修改了两个全局变量,然后进入performWork。这三个内部方法起名很有意思。scheduleWork意为<code>打算工作</code>,requestWork意为<code>申请工作</code>,performWork意为<code>努力工作(正式上班)</code></p><pre class="brush:php;toolbar:false">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 <p><span class="img-wrap"><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/944dc74785d9d477fd8444f5658d976d-1.png?x-oss-process=image/resize,p_40" class="lazy" alt="Detailed explanation of the fiber architecture of React16.2" ></span></p><p>我们终于进入performWorkOnRoot,performWorkOnRoot的作用是区分同步渲染还是异步渲染,expirationTime等于1,因此进入同步。导步肯定为false</p><pre class="brush:php;toolbar: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 <p><span class="img-wrap"><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/8f77c2afe11e03546d57c29e697ca86f-2.png?x-oss-process=image/resize,p_40" class="lazy" alt="Detailed explanation of the fiber architecture of React16.2" ></span></p><p>renderRoot也是怒长,React16代码的特点是许多巨型类,巨型方法,有JAVA之遗风。renderRoot只有前面几行是可能处理虚拟DOM(或叫fiber),后面都是错误边界的</p><pre class="brush:php;toolbar:false">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 <p><span class="img-wrap"><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/ac8d1569f29d1205551aecca3c3e6573-4.png?x-oss-process=image/resize,p_40" class="lazy" alt="Detailed explanation of the fiber architecture of React16.2" ></span><br>我们终于看到工作的代码了。 这个nextUnitOfWork 是renderRoot生成的<br>performUnitOfWork与beginWork的代码,里面会根据fiber的tag进入各种操作</p><pre class="brush:php;toolbar:false">//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) { //略 } }
相关推荐:
The above is the detailed content of Detailed explanation of the fiber architecture of React16.2. For more information, please follow other related articles on the PHP Chinese website!

C++中的众数函数详解在统计学中,众数指的是一组数据中出现次数最多的数值。在C++语言中,我们可以通过编写一个众数函数来找到任意一组数据中的众数。众数函数的实现可以采用多种不同的方法,下面将详细介绍其中两种常用的方法。第一种方法是使用哈希表来统计每个数字出现的次数。首先,我们需要定义一个哈希表,将每个数字作为键,出现次数作为值。然后,对于给定的数据集,我们遍

C++中的取余函数详解在C++中,取余运算符(%)用于计算两个数相除的余数。它是一种二元运算符,其操作数可以是任何整数类型(包括char、short、int、long等),也可以是浮点数类型(如float、double)。取余运算符返回的结果与被除数的符号相同。例如,对于整数的取余运算,我们可以使用以下代码来实现:inta=10;intb=3;

Vue.nextTick函数用法详解及在异步更新中的应用在Vue开发中,经常会遇到需要进行异步更新数据的情况,比如在修改DOM后需要立即更新数据或者在数据更新后需要立即进行相关操作。而Vue提供的.nextTick函数就是为了解决这类问题而出现的。本文就会详细介绍Vue.nextTick函数的用法,并结合代码示例来说明它在异步更新中的应用。一、Vue.nex

在Web应用程序中,缓存通常是用来优化性能的重要手段。Django作为一款著名的Web框架,自然也提供了完善的缓存机制来帮助开发者进一步提高应用程序的性能。本文将对Django框架中的缓存机制进行详解,包括缓存的使用场景、建议的缓存策略、缓存的实现方式和使用方法等方面。希望对Django开发者或对缓存机制感兴趣的读者有所帮助。一、缓存的使用场景缓存的使用场景

PHP-FPM是一种常用的PHP进程管理器,用于提供更好的PHP性能和稳定性。然而,在高负载环境下,PHP-FPM的默认配置可能无法满足需求,因此我们需要对其进行调优。本文将详细介绍PHP-FPM的调优方法,并给出一些代码示例。一、增加进程数默认情况下,PHP-FPM只启动少量的进程来处理请求。在高负载环境下,我们可以通过增加进程数来提高PHP-FPM的并发

在PHP开发中,有时我们需要判断某个函数是否可用,这时我们便可以使用function_exists()函数。本文将详细介绍function_exists()函数的用法。一、什么是function_exists()函数?function_exists()函数是PHP自带的一个内置函数,用于判断某个函数是否被定义。该函数返回一个布尔值,如果函数存在返回True,

Gin框架是目前非常流行的Go语言Web框架之一。作为一个轻量级的框架,Gin提供了丰富的功能和灵活的架构,使得它在Web开发领域中备受欢迎。其中一个特别重要的功能是模板渲染。在本文中,我们将介绍Gin框架的模板渲染功能,并深入了解它的实现原理。一、Gin框架的模板渲染功能Gin框架使用了多种模板渲染引擎来构建Web应用程序。目前,它支持以下几种模板引擎:

ORM(Object-RelationalMapping)框架是一种用于将面向对象编程语言中的对象模型与关系型数据库之间映射的技术。它使开发人员能够使用面向对象的方式操作数据库,而不需要直接操作SQL语言。在PHP开发领域中,ORM框架也得到了广泛的应用。本文将详细介绍PHP中的ORM框架使用方法。一、ORM框架的优点使用ORM框架有以下优点:1.提高开发


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SublimeText3 English version
Recommended: Win version, supports code prompts!

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

Zend Studio 13.0.1
Powerful PHP integrated development environment

Atom editor mac version download
The most popular open source editor

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.
