好的文章就像1990年代的香港电影一样无尽的。尽管本文写了18年,但似乎仍然对理解React Fiber的工作流程非常有帮助。一种API在最新版本的React中被放弃了,但它根本不会影响对整体过程的理解。遵循React源代码系列以了解React源代码
本文使用带有父组件和子组成部分的简单情况来证明在纤维体系结构中反应的内部过程,这些反应传输到子类中。
在我上一篇文章Fiber中:在React中新的协调算法的 - 深度概述中,我介绍了了解本文中介绍的更新过程的技术细节所需的基本知识。
我概述了本文中将使用的主要数据结构和概念,尤其是光纤节点,当前树和劳动树,副作用和副作用。。如果您还没有阅读,建议您从上一篇文章开始。
我还介绍了示例应用程序。该应用程序有一个按钮。单击按钮以增加屏幕上显示的数字:
这是一个简单的组件。渲染方法返回按钮和跨度的两个子元素。当您单击按钮时,将更新组件的状态。这将导致跨度元素的文本更新:
在这里,我在组件中添加了componentDidupdate。这是为了演示React如何在提交阶段添加副作用并调用ComponentDidupDate方法。
在本文中,我将介绍如何处理反应并构建副作用列表。我们将了解渲染和提交阶段的主要功能。
特别是,我们将在完整的函数中看到React已执行:
同时,在提交根部函数中,React将:
但是在此之前,让我们快速看一下在点击事件中调用setState时如何安排反应的时间。
请注意,您可以在不知道任何东西的情况下使用React。这篇文章是关于React的原则。
当我们单击按钮时,单击事件会触发,并反应执行我们在按钮中绑定的回调。在我们的应用程序中,它只是增加了计数器并更新状态:
每个React组件都有一个关联的更新程序,它充当组件和React内核之间的桥梁。这允许ReactDom,React Native,Server -Side渲染和测试实用程序以不同的方式实现SETSTATE。
在本文中,我们将使用Fiber Conecler探索ReactDor中的Updater对象的实现。对于ClickCounter组件,它是ClassComponentUpDater.it负责搜索光纤实例,将更新添加到队列和调度中。
当添加更新时,它们只需添加到更新队列中以在光纤节点上处理。在我们的示例中,与ClickCounter组件相对应的光纤节点的结构如下:
可以看出,updatequeue.firspdate.next.payload中的函数是我们在clickcounter component中传递给setState的回调。IT代表需要在渲染阶段处理的第一个更新
在我的上一篇文章中,工作周期的章节解释了全局变量NextUnitofwork的作用。特别是,它表明该变量保留了需要在WorkInprogress树中处理的光纤节点的引用。反应穿越光纤树时,它使用此变量来了解是否有尚未完成的光纤节点。
假设我们已经称为setState方法。回答将回调添加到setState中的clickcounter fiber节点的更新中,并启动调度。回答进入渲染阶段。它从renderroot函数的顶层中的hostroot fiber节点穿越。,它将退出(跳过)处理后的光纤节点,直到找到未完成的节点。这次,只有一个光纤节点要处理。
所有作业均在此光纤节点的克隆副本上执行,并且(副本)存储在光纤节点的替代字段中。如果尚未创建备用节点,则React将在function createworkinprogress.let中创建一个副本。我们假设变量NextUnitofwork指向ClickCounter Fiber节点的替代节点。
我们的光纤节点首先是由开始工作功能处理的。
因为纤维树中的每个纤维节点都是由开始工作函数处理的,所以如果要调试渲染阶段,这是中断点的好地方。我经常这样做,并根据光纤节点的类型添加条件断点
开始工作功能是一个大型开关语句。它确定需要通过标签通过纤维节点完成的工作类型,然后执行相应的功能以执行工作。在此示例中,CountClicks是类组件,因此采用了此分支:
我们输入UpdateClassComponent函数。根据组件是第一个渲染还是更新,React将创建一个实例或安装组件并更新
我们已经有ClickCounter组件的实例,因此我们输入UpdateClassInstance。这是React执行大部分工作的地方。以下是该功能中执行顺序的最重要操作:
在调用渲染方法之前,必须更新组件实例的状态和道具。由于渲染方法的输出取决于状态和尊敬。如果我们不这样做,则每次都会返回相同的结果。
这是此功能的简化版本:
我在上面的代码片段中删除了一些辅助代码。例如,在调用生命周期方法或添加生命周期方法的效果之前,React使用typeof操作员检查是否实现了相应的生命周期方法。示例,在添加效果之前,React将检查组件实例是否存在于componentDidupdate方法中。
好的,现在我们知道在渲染阶段执行了哪些操作。LET查看这些操作如何更改纤维节点上的值。当React开始工作时,ClickCounter组件的光纤节点看起来像这样:
工作完成后,我们最终在下面显示一个光纤节点:
花一些时间观察属性值的差异。
更新完成后,光纤。moizedState和fiber.updatequeue.basestate asterate astere asterate aster.reextate也更新了ClickCounter组件实例中的状态。
目前,队列中不再有更新,因此第一个update设置为null。重要的是,我们的效果tag属性已更改。它不再是0,它变为4.在二进制中,是100,它是100,这意味着第三名设置为1,这是更新的子效应标签的位:
总而言之,在使用ClickCounter Fiber(父)节点上工作时,React将调用神经前期生命周期方法以更新状态并定义相关的副作用。
完成后,React将输入finishClassComponent函数。这是React调用组件实例上的渲染方法,并将DOM DIFF算法应用于组件返回的子元素。该文档已高度汇总。这是相关部分:相关部分:
当比较同一类型的两个React DOM元素时,React将查看两者的属性,重复使用相同的基础DOM节点,仅更新可更改的属性。
但是,如果我们深入挖掘,我们可以理解它实际上将光纤节点与React元素进行了比较。但是我现在不会详细介绍它,因为该过程非常复杂。我会写一篇单独的文章以关注该过程子元素协调。
如果您渴望了解详细信息,请检查ConeceLechildRenarray功能,因为在我们的应用程序中,渲染方法返回了React Elect元素数组。
在这一点上,有两件重要的事情需要理解。首先,当React处理子元素协调过程时,它会创建或更新由Render Method返回的sub -React元素的光纤节点。引用当前光纤节点的第一个子节点。它将分配给Nextunitofwork并在以后的工作周期中处理。儿童元素的Props更新已在父组件中完成)。为此,它使用渲染方法返回的React元素中的数据。
例如,在React开始协调ClickCounter光纤的子元素之前,与跨度元素相对应的纤维节点如下:
如您所见,记者的属性和待挂接的属性为0.Below是渲染方法返回的跨度元素的结构:
如您所见,纤维节点与返回的React元素之间的道具之间存在差异。CreateWorkinProgress函数用于创建替代光纤节点。该功能中的React将将更新的属性从React元素复制到光纤节点。
因此,在ClickCounter组件的React完成的子元素协调之后,更新了SPAN纤维节点的dendingProps属性。它们匹配跨度元素的值。
稍后,当对跨度光纤节点执行React时,它会将fendingProps编写为记忆props,并添加效果以更新DOM。
好吧,这是React在React阶段执行的所有任务。无事可做,因此React将移至其兄弟节点,跨越光纤节点。根据此处描述的算法,它发生在完整的nitofwork函数中
现在,变量NextUnitofwork指向跨度光纤的备用节点,并且React开始处理它。类似于ClickCounter的步骤,我们从开始工作函数开始。
由于我们的跨度节点是hostComponent类型,因此这次在交换语句中,React使用此分支:
在updateHostComponent函数中结束。在同一时间,您还可以查看updateClassComponent函数称为classComponent.for functionalyComponent,它将是updateFunctionComponent等。
在我们的示例中,UpdateHostComponent函数对跨度节点没有任何重要的作用。
开始工作完成后,节点将输入完整的功能。但是在此之前,React需要更新跨度光纤上的MemoizedProps属性。您可能仍会记住ClickCounter组件的协调子元素时,React React更新pendendProps属性属性属性属性节点
因此,一旦完成跨度光纤节点的开始工作,React将更新备忘录:
然后调用完整的功能。像开始工作功能一样,完整的函数只是一个大的开关语句:
由于我们的跨度光纤节点是hostcomponent,因此它调用updateHostComponent函数。在此函数中,反应基本上执行以下操作:
在执行这些操作之前,跨度纤维节点如下所示:
工作完成后,看起来像这样:
请注意效果数据和updatequeue fields.effecttag之间的差异从0更改为4。这是二进制中的100,这意味着第三名设置为1,这是与更新的副作用相对应的标签类型。这是唯一的在下一个提交阶段,对此节点的反应需要做的作业。更新标准字段由用于更新的有效负载保存。
一旦React处理完成,ClickCounter及其子元素就完成了。现在可以将完整的备用树分配给Fiberroot的成品工作属性。这是一棵新树,需要刷新到屏幕上。它可以立即处理。在渲染阶段之后,也可以在浏览器的空闲时间进行处理。
在我们的示例中,由于两个跨度节点和ClickCounter组件都有副作用,因此React将添加到跨度纤维节点的链接到hostfiber的Fisteft属性。
React在完整的unitofwork函数中创建了副作用列表。这是带有纤维树的光纤树的外观,带有带有跨度节点文本的副作用并调用ClickCounter的副作用:
这是带有副作用的节点的线性列表:
此阶段从完整的函数开始。在开始执行任何作业之前,它将Fiberroot成品属性重置为null:
与渲染阶段不同,提交阶段始终是同步的,因此它可以安全地更新Hostroot,以表明该提交工作已经开始。
在提交阶段,React更新DOM并称为突变后生命周期,例如ComponentDidupDate。为此,它遍历了渲染阶段内置的副作用列表并应用它们。
我们在渲染阶段定义了跨度和clickcounter节点的以下效果:
ClickCounter的效果标签为5或二进制101,这意味着您需要调用componentDidupdate方法。还设置了最小效率位以表明光纤节点已在渲染阶段完成。
SPAN的效果标签是4或二进制100,它定义了需要更新的DOM节点的更新。对于SPAN元素,React需要更新元素的文本续签属性。
让我们看看React如何使用这些效果。用于应用效果的提示根函数由3个子功能组成:
这些子函数中的每一个都实现了一个周期,该周期遍历副作用的类型并检查效果的类型。当它找到与函数相关的效果时,它适用。在我们的示例中,它将称为componentdidupdate生命周期的方法ClickCounter组件并更新跨度元素文本。
第一个函数consotbeforemuontLifeCycles会找到快照效果并调用getsNapShotbeForeupDate.however,因为我们没有在ClickCounter组件上实现此方法,因此React在渲染阶段没有添加相应的快照效果。因此,在我们的示例中,此功能无济于事。
在下一步中,React执行Community -Hostefts函数。React React将跨度元素的文本从0更改为1。从与类组件相对应的相应节点没有任何DOM更新,无需处理ClickCounter Fiber。
此功能的目的是选择正确的效果类型并执行相应的操作。在我们的示例中,我们需要更新跨度元素上的文本,因此我们在此处使用“更新分支:
继续执行“提交功能”,然后我们最终输入更新的properperties函数。它使用渲染阶段添加的UpdateQueue数据来更新跨度元素的TextContent属性。
应用程序DOM更新后,React将成品树分配到Hostroot。它将替代树设置为当前树:
最后一个功能是commotalllifecycles.React称为突变后生命周期方法。在渲染阶段,React添加了ClickCounter组件的更新效果。这是communitylllifecles函数的效果之一,以查找并调用ComponentionDidupDate方法:
此函数将更新参考,但是由于我们没有任何此类功能,因此我们不会使用it.componentdidupdate方法在commitlifecycles函数中调用:
您还可以看到这是将组件方法用于呈现的组件的地方。
原始:https://juejin.cn/post/7099761425421172744