【9.21】ReactDom.render是如何串联渲染链路的(上、中、下)

81 阅读5分钟

这里截取的重要的部分记录,如需详细还请前往->链接地址

调用栈逻辑分层

三个阶段

  1. 初始化阶段
  2. render阶段
  3. commit阶段

image.png


初始化阶段

完成fiber树中基本实体的创建

image.png


legacyCreateRootFromDOMContainer:
作用是创建一个根 Fiber 节点,该节点表示渲染目标(通常是一个 DOM 容器)。这个根 Fiber 节点将会成为整个 Fiber 树的起点,负责协调渲染和更新工作。
legacyCreateRootFromDOMContainer的主要逻辑链路如下图:
image.png


fiberRoot 的关联对象是真实DOM的容器节点
rootFiber 则作为虚拟DOM的根节点存在

树的形式目前是->
image.png
在这之前,ReactDom.render都是同步渲染的


React渲染中有三种模式

  1. legacy:ReactDom.render(常用) [递归调用的方式处理组件的渲染,会有性能问题]
  2. blocking: ReactDom.createBlockingRoot(低频)
  3. concurrent:ReactDom.createRoot(React16以后)[可以同时处理多个任务以保持流畅性]

legacy的意义

React官方希望能够提供渐进的迁移策略帮我们能够更顺滑的过度到concurrent模式

开启异步渲染

image.png
image.png
开启之后:就可以使 React 能够更好地处理并发任务,并更好地适应不同的渲染环境和设备。在使用 Concurrent Mode 时,React 将尽力保持用户界面的流畅性,即使有多个任务在同时运行。


React16如果没有开启Concurrent,还能叫Fiber架构吗?

从动机上看
Fiber架构的设计确实为了 Concurrent 而存在
Fiber 架构在 React 中并不能够和异步渲染画严格的等号
它是一种同时兼容了同步渲染与异步渲染的设计

render阶段

image.png
1、图中:performSyncWorkOnRoot代表着 render 阶段的开始;
2、其中的beginWork、completeWork等等都是 render 的工作内容,不过它俩是一个递归的过程。回顾:
image.png

workInProgress的创建

它调用了renderRootSync,prepareFreshStack(重置一个新的堆栈环境)紧跟其后然后就是对createWorkInProgress的调用
image.png

下图为createWorkInProgress的主要过程:

image.png
image.png
image.png

  • createWorkInProgress 将调用 createFiber, workInProgress 是 createFiber 方法的返回值
  • workInProgress 的 alternate 将指向 current
  • current 的 alternate 将反过来指向workInProgress

下边为createFiber的实现

var createFiber = function (tag, pendingProps, key, mode) {
  return new FiberNode(tag,pendingProps, key, mode)
}

下边为workInProgress的实现(它就是current的副本,也就是rootFiber的副本)

workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode)

完成到这里:树的形式就变成了->
image.png

下图是 workLoopSync 的实现

image.png

  • 通过while循环反复判断workInProgress是否为空
  • 触发对beginWork的调用,进而实现对新 Fiber 节点的创建

beginWork 开启 Fiber 节点的创建

源码 如图

image.png
image.png
image.png
image.png
image.png

  • beginWork 的入参是一对用 alternate 连接起来的 workInProgress 和 current 节点
  • beginWork 的核心逻辑是根据 fiber 节点 (workInProgress)的tag属性的不同调用不同的节点创建函数

通过调用 reconcileChildren 方法,生成当前节点的子节点

image.png

childReconciler,处理 Fiber 节点的幕后操盘手

var reconcileChildFibers = ChildReconcile(true)
var mountChildFibers = childReconciler(false)

源码->

image.png
image.png
image.png
image.png
image.png

  • reconcileChildFibers 和 mountChildFibers 的不同,在于对副作用的处理不同
  • ChildReconciler 中定义了大量如 placeXXX、deleteXXX、updateXXX、reconcileXXX等这样的函数,这些函数覆盖了对 Fiber 节点的创建、增加、删除、修改等动作,将直接或简介被reconcilChildFibers 所调用
  • ChildReconciler 的返回值是一个名为 reconcileChildFibers 的函数,这个函数是一个逻辑分发器,它将很具入参的不同,执行不同的 Fiber 节点操作,最终返回不同的目标 Fiber 节点

对副作用的处理

image.png

newFiber.flags = Placement

flags 是什么?

它以前叫 effectTag
placeMent这里的effectTag是告诉渲染器:我这里需要新增DOM节点
effectTag 记录的是副作用的类型 ---什么是副作用?

  1. “数据获取、订阅或者修改DOM”等动作

**现在的树为-> **
image.png


Fiber 节点的创建过程梳理

image.png
image.png

Fiber 节点的构建过程

workLoopSync里performUnitOfWork的调用 生成新的Fiber节点

image.png

新建过程

image.png

Fiber 节点间是如何连接的?

  • 不同的 Fiber 节点之间
  • 将通过 child、return、sibling这 3 哥属性建立关系
  • 其中 child、 return记录的是父子关系
  • sibling 记录的是兄弟关系

如下图所示->
image.png

commit 阶段

completeWork

调用时机:

image.png
可以看出从performUnitOfWork到completeWork有以下链路:
注意:completeUnitOfWork是 completeWork开启的“工具人”
image.png

performUnitOfWork源码

image.png
image.png
image.png

completeWork源码

image.png
image.png
image.png
image.png
image.png
image.png
image.png

completeWork工作原理

1、completeWork工作内容

负责处理从 fiber 节点到 dom 节点的映射逻辑

2、completeWork有三个核心

  1. 创建 DOM 节点
  2. 将 DOM 节点插入到 DOM树
  3. 为 DOM 节点设置属性

3、创建好的 DOM 节点会被赋值给 workInProgress 节点的 stateNode 属性

4、将 DOM 节点插入到 DOM 树的操作是通过 appendAllChildren 函数来完成的

*实际上是将 子Fiber节点所对应的 DOM 节点 挂载到其父Fiber节点所对应的DOM节点里


completeUnitOfWork--开启收集EffectList的“大循环”

开启下次循环的原则:
当前的Fiber节点之所以会进入 completeWork 是因为“递无可递”了, 才会进入“归”的逻辑;
image.png
1、h1节点是递归过程中所触及的第一个叶子结点,也是其 兄弟节点中被遍历到的第一个节点;
2、对于h1节点的兄弟节点来说:当下第一要务是回去从beginWork开始走起
直到beginWork“递无可递”时,才能够执行completeWork的逻辑
3、在向下递归到 h1的过程中,div 必定已经被便利过了,也就是说div的“递”阶段(beginWork)已经执行完毕,只剩下“归”阶段的工作要处理了
4、确认没有待处理的兄弟节点之后,才转而处理父亲节点
所以:complete的处理是自底向上的


针对传入的当前节点,调用completeWork

将当前节点的副作用链插入到其父节点对应的副作用链中

以当前节点为起点,循环遍历其兄弟节点及其父节点

image.png
image.png


副作用链(effectList)的设计与实现

render阶段的工作目标是?

image.png
当render阶段结束后:“找不同” 的阶段也就结束了
commit阶段只负责实现更新,而不负责找

副作用链可以理解为render阶段的“工作成果”的一个集合

  • 都是当前Fiber节点的后代节点
  • 都有待处理的副作用

image.png
image.png


commit阶段的工作流 简析

  • bedore mutation 阶段, 这个阶段DOM节点还没有被渲染到界面上去
  • mutation, 这个节点负责DOM节点的渲染
  • layout,这个阶段处理DOM渲染完毕之后的收尾逻辑

它还会把fiberRoot的 current 指针指向workinProgress Fiber树