React源码系列(二)------ 首次挂载时completeWork

827 阅读3分钟

前言

本文主要内容是首次挂载时的completeWork。其实整个completeWork过程是最简单的,之所以不将更新时的也一起讲了,是因为不想将dom-diff部分的代码也融合进来,保持completeWork这部分代码的纯净,更好理解这个阶段都做了些什么。不过后面的流程图会有一个大概的更新时的completeWork的部分。

承接上文,看过我上一篇beginWork文章中的流程图,可以知道completeWork执行时机在这(如图)。我们现在就来完善这部分。

image.png

completeWork

一、做了什么

completeWork阶段主要就是根据fiber树,生成对应的真实dom树,每一个原生fiber节点上面(也就是fiber.tag为5或6的)都有一个stateNode属性,这个属性的内容就是自己的真实dom。

二、怎么做

completeWork主流程

completeWork.png

图中的workInProgress是一个全局变量,它就是当前fiber的替身,如果当前fiber的替身是null,那就创建一个新fiber替换掉这个null。因为本文是承接上文,就不过多解释这个workInProgress。

上图中,无论是创建真实dom还是将儿子的真实dom都挂载到自己的真实dom上都好理解,唯一可能有疑惑的点就是他们的结束节点:更新所有子节点上的副作用之和到自己的subtreeFlags上(下图这个位置)。

image.png

这里就要说说fiber上的这两个属性的作用了。

  • flags:标识自己身上是否有副作用。如果有,在commitRoot阶段会执行对应的副作用的操作。
  • subtreeFlags:标识自己的子节点是否有副作用。如果有,在commitRoot阶段会开始遍历自己的子节点,执行所有有副作用的子节点的对应的副作用的操作。

因为在commitRoot阶段,我们能获取到的信息是根fiber,它底下是否有副作用要执行,就要通过底下的节点们不断收集自己的subtrssFlags,最后反馈到根fiber上。我们来看一个示例。

// 示例的结构
<h1><span>hello world</span></h1>

flags和subtreeFlags.png

看绿色框部分,每一次自己的subtreeFlags都为子节点的subtreeFlags + 子节点的flags,以此方式一直累加到root上。这样在commitRoot阶段,根fiber看到自己的subtreeFlags有副作用就知道要遍历自己的子节点去执行他们的副作用。同样的,如果根fiber看到自己的subtreeFlags没有副作用就不需要管子节点了。说到底,这里属于一个优化,如果没有subtreeFlags,每次commitRoot就都需要进行子节点的遍历。

到此,首次挂载时的completeWork就结束了,内容很少,也很好理解,有兴趣的读者,可以简单调试下文末贴的代码。

三、结果

结合上上文的beginWork,我们来看看整个流程。

// 结构
<div>
  react:
  <span>hello world</span>
</div>

输出的每行的内容为:阶段 fiber.tag 内容/节点类型

0c3b6353f3d2a4af729079c63ad6ebd.png

依然是一个深度优先的逻辑,先完成最底下的子节点的completeWork(文本节点: react:和span节点),然后是上一层的div节点,最后是节点。

结尾

这里是只有到completeWork阶段代码的仓库分支,大家可亲自用这代码进行调试,过一遍整个completeWork流程。

ps:这部分的代码,函数名大多是基本与源码一致,若觉得笔者有错误的地方或想拓展哪部分,可通过函数名直接在源码中搜索。

这里是整个React源码,供大家拓展。

上一篇:beginWork

下一篇:commitRoot