前言

React是一个用于构建界面的JavaScript库。它的外围是跟踪组件状态变动并将更新后的状态更新到屏幕上。在React中,咱们把这个过程称为 reconciliation (协调)。通过调用setState办法,React查看状态或属性是否已更改,并在UI层上更新。

背景介绍

首先看一个简略的例子:

class ClickCounter extends React.Component {    constructor(props) {        super(props);        this.state = {count: 0};        this.handleClick = this.handleClick.bind(this);    }    handleClick() {        this.setState((state) => {            return {count: state.count + 1};        });    }    render() {        return [            <button key="1" onClick={this.handleClick}>Update counter</button>,            <span key="2">{this.state.count}</span>        ]    }}

这是一个简略的计数器的例子, 点击按钮,组件的状态就会在处理程序中更新,组件的状态的更新反过来会引起span元素的内容更新。

上面是在协调阶段,React外部会有各种流动。以下是计数器在协调阶段所做的操作:

  1. 更新state的count属性
  2. 查看并比拟ClickCounter的子代以及props
  3. 更新span元素

协调期间还会执行其余流动,例如调用生命周期办法,更新ref。这些流动在Fiber架构中统称为"work"(工作)。work的类型取决于React元素的类型。React元素有多种类型,比方类组件,函数组件,Portals,DOM节点等。而React元素类型则是由React.createElement的第一个参数决定。React.createElement函数在render创立元素中调用。

React.createElement到Fiber

JSX编译

<button key="1" onClick={this.onClick}>Update counter</button><span key="2">{this.state.count}</span>

JSX在通过编译后,会失去如下的后果。这是render办法真正返回的后果

class ClickCounter {    ...    render() {        return [            React.createElement(                'button',                {                    key: '1',                    onClick: this.onClick                },                'Update counter'            ),            React.createElement(                'span',                {                    key: '2'                },                this.state.count            )        ]    }}

React.createElement函数会返回如下的后果:

[    {        $$typeof: Symbol(react.element),        type: 'button',        key: "1",        props: {            children: 'Update counter',            onClick: () => { ... }        }    },    {        $$typeof: Symbol(react.element),        type: 'span',        key: "2",        props: {            children: 0        }    }]
  • $$typeof属性,用于将它们标示为react元素
  • type,key,props,用于形容元素

而对于组件<ClickCounter>的元素,它没有props和key:

{    $$typeof: Symbol(react.element),    key: null,    props: {},    ref: null,    type: ClickCounter}

Fiber节点

在协调期间,render办法返回的React元素会被合并到Fiber节点树之中。每一个React元素都有对应的Fiber节点。与React元素不同,Fiber不会在每一次渲染的时候从新创立。Fiber会保留组件的状态和DOM。

后面探讨过,依据不同的React元素类型,会执行不同的流动。例如,对于Class组件会调用生命周期办法以及render办法。而对于DOM节点,它执行DOM mutation。因而,每个React元素都被转换为相应类型的Fiber节点。节点形容了须要实现的"work"。

能够将Fiber节点看作一种数据解构,示意一个work单元。,Fiber架构还提供了一种跟踪、调度、暂停和停止work的办法。

React会在首次将React元素转换为Fiber节点时,应用createFiberFromTypeAndProps函数创立Fiber节点。在更新阶段会复用Fiber节点,并应用React元素上的数据更新Fiber节点上的属性。亦或者挪动,删除Fiber节点。

React源码中的ChildReconciler函数蕴含了Fiber节点中所有的work

React会为每一个React元素创立一个Fiber节点,咱们会失去一个Fiber节点树

Fiber节点树是通过链表的模式存储的,每一个Fiber都领有child(第一个子节点的援用),sibling(第一个兄弟节点的援用)和return(父节点的援用),来示意层级关怀。更多内容请参考这篇文章React Fiber为什么应用链表来设计组件树

current tree 和 progress tree

在第一次渲染实现后,React会生成一个Fiber树。该树映射了应用程序的状态,这颗树被称为current tree。当应用程序开始更新时,React会构建一个workInProgress tree, workInProgress tree映射了将来的状态。

所有的"work"都是在workInProgress tree上的Fiber节点上进行的。当React开始遍历current tree时,它会为每一个现有的Fiber节点创立一个备份(alternate字段),alternate节点形成了workInProgress tree。当所有更新和相干的"work"实现。workInProgress tree会刷新到屏幕上。workInProgress tree此时变为了current tree

React的外围准则之一是"一致性", 它总是一次性更新DOM, 不会显示局部后果. workInProgress就是一个用户不可见的"草稿", React在它下面解决所有组件, 解决实现后将它再刷新到界面上.

在React的源码中,有很多从workInProgress treecurrent tree中获取Fiber节点的函数。比方上面这个函数签名

function updateHostComponent(current, workInProgress, renderExpirationTime) {    ...}

workInProgress tree的Fiber节点领有current tree对应节点的援用。反之亦然。

副作用

咱们能够将React组件视为应用state和props计算UI的函数。其余的流动,比方手动批改DOM,调用生命周期都应该被视作一种副作用。在React的文档中也提及了这一点

你之前可能曾经在 React 组件中执行过数据获取、订阅或者手动批改过 DOM。咱们对立把这些操作称为“副作用”(side-effects),或者简称为“作用”(effects)。因为它们会影响其余组件,并且在渲染期间无奈实现。

大多数state和props的更新都会导致副作用。利用effects是一种work类型。因而Fiber节点是一种跟踪更新和effects的便捷机制,每一个Fiber节点都有与之相关联的effects。它们被编码在effectTag字段之中

Fiber中的effects定义了解决更新之后须要做的"work"。对于DOM元素,"work"蕴含了增加,更新,删除。对于类组件,包含了更新ref,调用componentDidMount和componentDidUpdate生命周期办法。还有其余effects对应于其余类型的Fibber。

Effects list

React解决更新十分快。为了达到更好的性能程度,采纳了一些乏味的技术。其中之一就是将具备effects的Fiber节点,构建为线性列表,以不便疾速迭代。迭代线性列表要比迭代树快的多,因为不须要迭代没有side-effects的节点。

effects list的目标是是标记出具备DOM更新,或其它与之关联的其余effects的节点。effects list是finishedWork树的子集。在workInProgress treecurrent tree中应用nextEffect属性链接在一起。

丹·阿布拉莫夫(Dan Abramov)将Effects list提供了一个比喻。将Fiber设想成一颗圣诞树,用圣诞灯将所有无效的节点连贯在一起。

为了可视化这一点,让咱们设想上面的Fiber树,其中高亮显示的节点有一些“work”要做。

例如,咱们的更新导致将c2插入到DOM中,d2和c1更改属性,b2触发生命周期办法。 Effects list列表将把它们链接在一起,这样React就能够遍历时跳过其余节点。

能够看到具备effects的节点如何链接在一起。当遍历节点时,React应用firstEffect指针确定列表的起始地位。上图的Effects list能够用下图示意

Fiber节点树的根

React利用都有一个或者多个充当容器的DOM元素

const domContainer = document.querySelector('#container');ReactDOM.render(React.createElement(ClickCounter), domContainer);

React会为容器创立FiberRoot对象,能够应用容器的DOM援用拜访Fiber root对象:

// Fiber root对象const fiberRoot = query('#container')._reactRootContainer._internalRoot

Fiber root是React保留对Fiber树援用的中央,Fiber树存储在Fiber root对象的current属性中

// Fiber树const hostRootFiberNode = fiberRoot.current

Fiber树的第一个节点是一种非凡的类型节点,叫做HostRoot。它在外部创立,是最顶层组件的父组件。通过HostRoot节点的stateNode属性能够拜访FiberRoot节点.

// Fiber root对象const fiberRoot = query('#container')._reactRootContainer._internalRoot// hostRootconst hostRootFiberNode = fiberRoot.current// truehostRootFiberNode.stateNode === fiberRoot

咱们能够从HostRoot来拜访和摸索整个Fiber树。或者能够通过组件的实例中取得单个Fiber节点

compInstance._reactInternalFiber

Fiber节点构造

ClickCounter组件的Fiber节点构造:

{    stateNode: new ClickCounter,    type: ClickCounter,    alternate: null,    key: null,    updateQueue: null,    memoizedState: {count: 0},    pendingProps: {},    memoizedProps: {},    tag: 1,    effectTag: 0,    nextEffect: null}

span DOM元素的Fiber节点构造:

{    stateNode: new HTMLSpanElement,    type: "span",    alternate: null,    key: "2",    updateQueue: null,    memoizedState: null,    pendingProps: {children: 0},    memoizedProps: {children: 0},    tag: 5,    effectTag: 0,    nextEffect: null}

Fiber节点上有很多字段。咱们之前曾经形容了alternate(备份节点),effectTag(记录与之关联的effects), nextEffect(连贯具备effects的Fiber节点使其成为线性节点)

stateNode属性

保留对class组件实例的援用, DOM节点或其余与Fiber节点相关联的React元素类实例的援用。一般来说, 咱们能够说这个属性被用于保留与以后Fiber相干的本地状态。

type属性

定义与此Fiber节点相关联的函数或者类。对于class组件,type属性指向构造函数。对于DOM元素,type属性指向HTML标记。我常常用这个字段来判断这个Fiber节点与那个元素相干。

tag属性

定义Fiber节点的类型。在协调期间应用它确定须要做的"work"。如之前所述"work"取决于React元素的类型。createFiberFromTypeAndProps函数将React元素映射成绝对应的Fiber节点类型。

在咱们的例子中。ClickCounter的tag为1,示意为ClassComponent。span的tag为5,标记为HostComponent。

updateQueue属性

state更新和回调,DOM更新的队列。

memoizedState属性

用于创立输入Fiber的state。在解决更新的时候,它映射的是以后界面上出现的state。

memoizedProps属性

在上一次渲染过程中用来创立输入的Fiber props。

pendingProps属性

曾经更新后的Fiber props。须要用于子组件和DOM元素。

key属性

一组children中的惟一标示。帮忙React确定那些产生了更改,新增或删除。更具体的解释在这里

总结

残缺的Fiber构造, 能够在这里看到,在下面的阐明省略了很多的字段比方child,sibling并return。这三个字段是形成链表树结构的要害。以及expirationTime、childExpirationTime和mode,这些字段是特定于Scheduler的。

通用算法

React分两个阶段执行work:render(渲染)和 commit(提交)

在render(渲染)阶段,React将更新利用于通过setState或React.render调度的组件, 并找出须要在UI中更新的内容。

如果是初始渲染,React将为render办法返回的每一个元素创立新的Fiber节点。在之后的更新中,将从新应用和更新现有的Fiber节点。

render阶段会构建一个带有side-effects(副作用)的Fiber节点树。effects形容了下一个commit(提交)阶段须要实现的“work”。在commit(提交)阶段,React会应用标记有effects的Fiber节点并将其利用于实例上。遍历Effects list执行DOM更新和其余对用户可见的更改。

请切记,render阶段的工作是能够异步执行的,React依据可用工夫解决一个或者多个Fiber节点。当产生一些更重要的事件时,React会进行并保留已实现的工作。等重要的事件解决实现后,React从中断处持续实现工作。然而有时可能会放弃曾经实现的工作,从顶层从新开始。此阶段执行的工作是对用户是不可见的,因而能够实现暂停。然而在commit(提交)阶段始终是同步的它会产生用户可见的变动, 例如DOM的批改. 这就是React须要一次性实现它们的起因。

调用生命周期函数应用React的“work”之一。在render阶段调用这些生命周期办法:

  • UNSAFE_componentWillMount (deprecated)
  • UNSAFE_componentWillReceiveProps (deprecated)
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • UNSAFE_componentWillUpdate (deprecated)
  • render

因为render阶段不会产生DOM更新之类的副作用,因而React能够异步地对组件进行异步解决更新(甚至可能在多个线程中进行)。然而带有UNSAFE_前缀的生命周期函数经常会被误用,开发者会把副作用增加到这些生命周期函数中。这可能会导致异步渲染呈现问题

在commit阶段调用这些生命周期办法,这些生命周期办法在commit阶段执行,所以它们可能蕴含副作用并波及DOM更新。

  • getSnapshotBeforeUpdate
  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

渲染(render)阶段

协调算法应用renderRoot函数从最顶层的HostRoot节点开始,跳过曾经解决过的节点,直到找到work未实现的节点为止。例如, 当在组件树深处调用setState办法, React 从顶部开始疾速的跳过所有父级节点间接取得调用setState办法的组件。

WorkLoop

所有的Fiber节点在render阶段都会在WorkLoop中被解决。这是循环同步局部的实现:

function workLoop(isYieldy) {  if (!isYieldy) {    while (nextUnitOfWork !== null) {      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);    }  } else {...}}

在下面的代码中,nextUnitOfWork放弃了对workInProgress tree中一个有工作要解决的Fiber节点的援用。在React遍历Fiber树时,会应用nextUnitOfWork判断是否有未实现"work"的Fiber节点。当节点解决实现“work”后,nextUnitOfWork会指向下一个Fiber节点的援用或者为null。当nextUnitOfWork为null时,React会退出WorkLoop,并筹备进入到commit阶段。

有四个次要的办法用于遍历树,并启动或实现工作:

  • performUnitOfWork
  • beginWork
  • completeUnitOfWork
  • completeWork

为了演示如何应用它们。请查看上面遍历Fiber树的演示动画。演示动画中应用了这些函数的简化实现。咱们能够通过演示看到,首先解决子节点的“work”,而后解决父节点的“work”。

应用直线连贯代表同级,应用折线连贯的代笔子级

逐渐拆分下React遍历Fiber树的过程(首先解决子节点的“work”,而后解决父节点的“work”):

  1. beginWork a1
  2. beginWork b1,completeWork b1
  3. beginWork b2
  4. beginWork c1
  5. beginWork d1, completeWork d1
  6. beginWork d2, completeWork d2
  7. completeWork c1
  8. completeWork b2
  9. beginWork b3
  10. beginWork c2,completeWork c2
  11. completeWork b3
  12. completeWork a1

这个是视频的连贯, 从概念上将"begin"看成进入组件,“complete”看成来到组件。

咱们首先看下beginWork和performUnitOfWork这两个函数:

function performUnitOfWork(workInProgress) {    let next = beginWork(workInProgress);    if (next === null) {        next = completeUnitOfWork(workInProgress);    }    return next;}function beginWork(workInProgress) {    console.log('work performed for ' + workInProgress.name);    return workInProgress.child;}

performUnitOfWork从workInProgress tree中接管一个Fiber节点。而后调用beginWork开始解决Fiber节点的work。为了演示,这里只是log了Fiber节点的name字段示意work曾经实现。函数beginWork总是返回指向循环中下一个子节点或null。

如果有下一个子节点, 它将在workLoop函数中调配给nextUnitOfWork。如果没有子节点,React就晓得了达到了分支的结尾。就会实现以后Fiber节点的work。React会执行它兄弟节点的工作,最初回溯到父节点。这是在completeUnitOfWork中实现的。

function completeUnitOfWork(workInProgress) {    while (true) {        let returnFiber = workInProgress.return;        let siblingFiber = workInProgress.sibling;        nextUnitOfWork = completeWork(workInProgress);        if (siblingFiber !== null) {            // 如果有同级,则返回它。以继续执行同级的工作            return siblingFiber;        } else if (returnFiber !== null) {            // 回溯到上一级            workInProgress = returnFiber;            continue;        } else {            // 曾经到了root节点            return null;        }    }}function completeWork(workInProgress) {    console.log('work completed for ' + workInProgress.name);    return null;}

当workInProgress节点没有子节点时,会进入此函数。在实现以后Fiber的工作后,会查看是否有兄弟节点。如果有,返回同级的兄弟节点的指针,调配给nextUnitOfWork。React将会从兄弟节点开始工作。只有解决完子节点所有分支之后, 才会回溯到父节点(所有子节点解决实现后,才会回溯到父节点)。

从实现能够看出,completeUnitOfWork次要用于迭代,次要工作都是beginWork和completeWork函数中进行的。

残缺的示例

这里是残缺的示例(beginWork,performUnitOfWork,completeUnitOfWork,completeWork的繁难实现)

// 首先构建链表树const a1 = {name: 'a1', child: null, sibling: null, return: null};const b1 = {name: 'b1', child: null, sibling: null, return: null};const b2 = {name: 'b2', child: null, sibling: null, return: null};const b3 = {name: 'b3', child: null, sibling: null, return: null};const c1 = {name: 'c1', child: null, sibling: null, return: null};const c2 = {name: 'c2', child: null, sibling: null, return: null};const d1 = {name: 'd1', child: null, sibling: null, return: null};const d2 = {name: 'd2', child: null, sibling: null, return: null};a1.child = b1;b1.sibling = b2;b2.sibling = b3;b2.child = c1;b3.child = c2;c1.child = d1;d1.sibling = d2;b1.return = b2.return = b3.return = a1;c1.return = b2;d1.return = d2.return = c1;c2.return = b3;// 以后的指针是a1let nextUnitOfWork = a1;workLoop();// 开始工作循环function workLoop() {    while (nextUnitOfWork !== null) {        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);    }}function performUnitOfWork(workInProgress) {    let next = beginWork(workInProgress);    if (next === null) {        next = completeUnitOfWork(workInProgress);    }    return next;}function beginWork(workInProgress) {    log('work performed for ' + workInProgress.name);    return workInProgress.child;}function completeUnitOfWork(workInProgress) {    while (true) {        let returnFiber = workInProgress.return;        let siblingFiber = workInProgress.sibling;        nextUnitOfWork = completeWork(workInProgress);        if (siblingFiber !== null) {            return siblingFiber;        } else if (returnFiber !== null) {            workInProgress = returnFiber;            continue;        } else {            return null;        }    }}function completeWork(workInProgress) {    log('work completed for ' + workInProgress.name);    return null;}function log(message) {  let node = document.createElement('div');  node.textContent = message;  document.body.appendChild(node);}

提交(commit)阶段

提交阶段从completeRoot开始。这是React更新DOM,调用getSnapshotBeforeUpdate,componentDidMount,componentDidUpdate,componentWillUnmount等生命周期的中央。

React进入这一阶段时,有两颗树(workInProgress tree和current tree)以及effects list。current tree示意了以后屏幕上出现的状态。render阶段遍历current tree时会生成另一颗树,在源码中被称为finishWork或workInProgress,示意将来须要在屏幕上出现的状态。workInProgress treecurrent tree构造相似。

调试时,如何获取current tree以及workInProgress tree?
// current tree// 从容器对象上获取FiberRoot对象const fiberRoot = query('#container')._reactRootContainer._internalRoot// 获取current treeconst currentTree = fiberRoot.current// 获取workInProgress treeconst workInProgressTree = fiberRoot.current.alternate

提交(commit)阶段,次要执行commitRoot函数,执行以下的操作:

  1. 在有Snapshot标记的Fiber节点上调用getSnapshotBeforeUpdate生命周期办法。
  2. 在有Deletion标记的Fiber节点上调用componentWillUnmount生命周期办法。
  3. 执行所有的 DOM 插入, 更新, 删除。
  4. workInProgress tree设置为current tree
  5. 在有Placement标记的Fiber节点上调用componentDidMount生命周期办法。
  6. 在有Update标记的Fiber节点上调用componentDidUpdate生命周期办法。

在调用getSnapshotBeforeUpdate办法后,React将commit,Fiber树中所有的副作用。分为两步:

第一步,执行所有的DOM插入,更新,删除和ref卸载。而后将workInProgress tree设置为current tree树。这是在第一步实现之后,第二步之前实现的。因而在componentWillUnmount生命周期办法在执行期间,状态仍然是更新之前的。而componentDidMount/componentDidUpdate执行时的状态是更新之后的。第二步,执行其余生命周期办法和ref回调,这些办法作为独自的过程被执行。

commitRoot办法的预览:

function commitRoot(root, finishedWork) {    // 用来执行getSnapshotBeforeUpdate    commitBeforeMutationLifecycles()    // 用户更新DOM,以及执行componentWillUnmount    commitAllHostEffects();    root.current = finishedWork;    // 调用componentDidUpdate和componentDidMount生命周期的中央    commitAllLifeCycles();}

这些子函数,外部都蕴含了一个循环。循环遍历effects list,并查看effects的类型。当发现类型和子函数的目标雷同时,就利用它。

Pre-mutation lifecycle methods

遍历effects list,并查看节点是否具备Snapshot effect的源代码:

function commitBeforeMutationLifecycles() {    while (nextEffect !== null) {        const effectTag = nextEffect.effectTag;        if (effectTag & Snapshot) {            const current = nextEffect.alternate;            commitBeforeMutationLifeCycles(current, nextEffect);        }        nextEffect = nextEffect.nextEffect;    }}

如果是class组件,调用getSnapshotBeforeUpdate生命周期办法。

DOM updates

commitAllHostEffects是React执行DOM更新的中央。React会把componentWillUnmount作为commitDeletion删除过程中的一部分。

function commitAllHostEffects() {    switch (primaryEffectTag) {        case Placement: {            commitPlacement(nextEffect);            ...        }        case PlacementAndUpdate: {            commitPlacement(nextEffect);            commitWork(current, nextEffect);            ...        }        case Update: {            commitWork(current, nextEffect);            ...        }        case Deletion: {            commitDeletion(nextEffect);            ...        }    }}

Post-mutation lifecycle methods

commitAllLifecycles是React调用所有残余生命周期办法componentDidUpdate和componentDidMount的中央。

总结

React源码很简单,Max Koretskyi的这篇文章内容也很多,所有总结下这篇博客的要点:

  1. React的外围是跟踪组件状态变动并将更新后的状态更新到屏幕上。在React中,咱们把这个过程称为 reconciliation (协调)。
  2. 协调期间的各种流动在Fiber架构中统称为work(工作),work的类型取决于React元素的类型,React元素的类型取决于React.createElement函数的第一个参数类型。
  3. 协调期间React元素会被合并到Fiber节点树之中,每一种React元素都会一种对应的Fiber节点。Fiber节点不会反复创立,会在第一次渲染后重用。
  4. React会为每一个React元素创立一个Fiber节点,咱们会失去一个Fiber节点树。Fiber节点树是通过链表的模式存储的。每一个Fiber节点都领有child,sibling和return字段,用于中断复原遍历。
  5. 在首次渲染时会生成一棵Fiber树。被称为current tree。当开始更新时,React会构建一个workInProgress tree
  6. current tree代表了以后的状态,workInProgress tree代表了将来的状态。
  7. 在commit阶段workInProgress tree会被设置为current tree
  8. React在遍历current tree时,会为每一个Fiber节点创立一个alternate字段,alternate字段保留了Fiber节点的备份,alternate字段上保留的备份Fiber节点形成了workInProgress tree
  9. 数据获取、订阅或者手动批改过DOM都统称为副作用(side-effects)或者简称为“作用”(effects)。因为它们会影响其余组件,并且在渲染期间无奈实现。
  10. effects是一种work类型。因而Fiber节点是一种跟踪更新和effects的便捷机制,每一个Fiber节点都有与之相关联的effects。它们被编码在effectTag字段之中。Fiber中的effects定义了解决更新之后须要做的"work"。对于DOM元素,"work"蕴含了增加,更新,删除。对于类组件,包含了更新ref,调用componentDidMount和componentDidUpdate生命周期办法。还有其余effects对应于其余类型的Fiber。
  11. React会将具备effects的Fiber节点,构建为线性列表,以不便疾速迭代。firstEffect是列表的开始地位,应用nextEffect属性将节点链接在一起(构建成线性列表)。
  12. 能够通过容器DOM元素,获取FiberRoot对象query('#container')._reactRootContainer._internalRoot
  13. FiberRoot对象的current属性,是current tree的第一个节点,被称为hostRoot。fiberRoot.current
  14. hostRoot节点的stateNode属性,指向FiberRoot对象。
  15. 获取workInProgress tree, fiberRoot.current.alternate
  16. Fiber节点构造见上文。
  17. React分两个阶段执行work:render(渲染)和 commit(提交)
  18. render阶段的工作是能够异步执行的,React依据可用工夫解决一个或者多个Fiber节点。当产生一些更重要的事件时,React会进行并保留已实现的工作。等重要的事件解决实现后,React从中断处持续实现工作。然而有时可能会放弃曾经实现的工作,从顶层从新开始。此阶段执行的工作是对用户是不可见的,因而能够实现暂停。
  19. commit(提交)阶段始终是同步的它会产生用户可见的变动, 例如DOM的批改. 这就是 React须要一次性实现它们的起因。
  20. 所有的Fiber节点在render阶段都会在WorkLoop中被解决。WorkLoop中次要有四个函数,performUnitOfWork,beginWork,completeUnitOfWork,completeWork。WorkLoop首先解决子节点的“work”,所有子节点解决实现后,回溯而后解决父节点的“work”。从概念上将"begin"看成进入组件,“complete”看成来到组件。
  21. 提交(commit)阶段分为两步实现。第一步,执行所有的DOM插入,更新,删除和ref卸载以及执行componentWillUnmount生命周期办法。第二步,执行其余生命周期办法和ref回调,这些办法作为独自的过程被执行。第一步和第二步两头会将workInProgress tree设置为current tree树。

参考

  • Inside Fiber: in-depth overview of the new reconciliation algorithm in React