ReactDOM.render串联渲染链路(二)

1,305 阅读3分钟

前言


前一篇文章ReactDOM.render串联渲染链路(一),我们梳理了渲染链路的初始化阶段和render阶段的前半段,这篇文章我们来看看render阶段的后半段和commit阶段。

render后半段


completeWork

前面有说道,在render阶段,这个过程中,穿插了大量了beginWork、completeWork调用,这两个方法串联起来就是一个模拟递归的过程。这个两个方法就是render的工作内容。接下来我们来看看completeWork。 在这render整个阶段beginWork和completeWork基本都是成对存在的,beginWork负责Fiber节点的创建,而competeWork负责Fiber阶段转换为真实Dom。

completeWork:前

if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    startProfilerTimer(unitOfWork);
    // 创建当前节点的子节点
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
  } else {
    // 创建当前节点的子节点
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
  }
if (next === null) {
    // 调用 completeUnitOfWork
    completeUnitOfWork(unitOfWork);
  } else {
    // 将当前节点更新为新创建出的 Fiber 节点
    workInProgress = next;
  }

performUnitOfWork中,先通过beginWork来创建当前页面的子节点,然后判断创建的子阶段是否为空,如果为空(说明这个节点没有子节点, 是一个叶子节点)。因此这种情况下,就会调用 completeUnitOfWork,执行当前节点对应的 completeWork 逻辑。

completeWork:后

completeWork中和beginWork的执行如出一辙,根据workInProgress tag(一共有十余种)的不同,执行不同的逻辑。这里我们重点来看看HostComponent case:

其实在render过程中,存在两棵树,一颗就是大名鼎鼎的workInProgress树,一颗就是current树。这两棵树workInProgress树表示”当前正在render的树“,current树表示”已经render完成的树“。

workInProgress 节点和 current 节点之间用 alternate 属性相互连接。在组件的挂载阶段,current 树只有一个 rootFiber 节点,并没有其他内容

completeWork总结
  • completeWork目的:将Fiber映射为真实DOM
  • completeWork关键工作
    • 创建DOM
    • 插入DOM树
    • 设置DOM属性
completeUnitOfWork:开启大循环

completeUnitOfWork主要的作用是开启一个大循环,在里面循环如下的操作:

  1. 针对当前阶段,调用completeWork
  2. 将当前节点的副作用链(EffectList)插入到其父节点对应的副作用链(EffectList)中。
  3. 以当前节点为起点,循环遍历其兄弟节点及其父节点,当遍历到兄弟节点时,将 return 掉当前调用,触发兄弟节点对应的 performUnitOfWork 逻辑;而遍历到父节点时,则会直接进入下一轮循环,也就是重复 1、2 的逻辑。

在整个ReactDOM.render的过程中,render节点是最为重要的,也是最难理解的,我感觉熟悉知道就行。

commit 阶段


commit 分为三个阶段

  • before mutation
  • mutation
  • layout
before mutation

before mutation 阶段,这个阶段 DOM 节点还没有被渲染到界面上去,过程中会触发 getSnapshotBeforeUpdate,也会处理 useEffect 钩子相关的调度逻辑。

mutation

这个阶段负责 DOM 节点的渲染。在渲染过程中,会遍历 effectList,根据 flags(effectTag)的不同,执行不同的 DOM 操作。

layout

这个阶段处理 DOM 渲染完毕之后的收尾逻辑。比如调用 componentDidMount/componentDidUpdate,调用 useLayoutEffect 钩子函数的回调等。除了这些之外,它还会把 fiberRoot 的 current 指针指向 workInProgress Fiber 树。

总结


通过两篇文章大致的梳理了一下,ReactDOM.render的渲染链路。

  • 初始化阶段
  • render阶段
  • commit阶段

完成了对 ReactDOM.render 调用栈的分析。表面上剖析的是首次渲染的渲染链路,实际上将包括同步模式下的挂载、更新链路(与挂载链路的调用栈非常相似)都串联了一遍。