前言
前一篇文章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主要的作用是开启一个大循环,在里面循环如下的操作:
- 针对当前阶段,调用completeWork
- 将当前节点的副作用链(EffectList)插入到其父节点对应的副作用链(EffectList)中。
- 以当前节点为起点,循环遍历其兄弟节点及其父节点,当遍历到兄弟节点时,将 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 调用栈的分析。表面上剖析的是首次渲染的渲染链路,实际上将包括同步模式下的挂载、更新链路(与挂载链路的调用栈非常相似)都串联了一遍。