performSyncWorkOnRoot 开启 render 阶段
commitRoot 开启 commit 阶段
初始化阶段
这阶段的目的是:
完成 Fiber 基本实体的创建
current 属性:
FiberNode 是 Fiber 节点对应的对象类型
- fiberRoot 是真实 DOM 的容器节点
-
初始化的行为:
unbatchedUpdates 直接调用传入的回调 fn,fn 是针对 updateContainer 的调用,主要做了三件事 请求当前 Fiber 节点的 lane(优先级)
- 结合 lane 创建当前 Fiber 节点为的 update 对象,并将其入队
调度当前节点(rootFiber) :::info 在 ReactDOM.render 发起前的前次渲染链路中,这些意义都不大,国灰这个渲染过程其实是同步的 ::: 因为会跑到 performSyncWorkOnRoot
performSyncWorkOnRoot 是 render 阶段的起点,render 阶段是任务就是完成构建 Fiber 树
为何 ReactDOM.render 触发的首次渲染是个同步的过程呢?
因为目前的 React16 / 17 都会有三种模式:
legacy
ReactDOM.render(<App />, rootNode);
- blocking
ReactDOM.createBlockingRoot(rootNode).render(<App />);
- 作为迁移至 concurrent 的第一个步骤
- concurrent
ReactDOM.createRoot(rootNode).render(<App />);
这个 React 官方想提供渐进的迁移方式,长远来看模式的数量会收敛,不考虑不同的模式
Fiber 架构一定是异步渲染吗?
从动机上看,Fiber 架构的设计确实是为了 Concurrent 存在,Fiber 架构在 React 中并不能和异步渲染画严格等号。
它是一种同时兼容了同步渲染与异步渲染的设计
render 阶段
React15 下的调和过程是一个递归的过程,在 ReactDOM.render 触发同步模式下仍然是一个深度优先搜索过程
- beginWork 将创建新的 Fiber 节点
completeWork 负责将 Fiber 节点为映射为 DOM 节点
prepareFreshStack 重置一个新的对象环境
createWorkInProgress 调用 createFiber
workInProgress 是 createFiber 方法的返回值- FiberNode 类型
- workInProgress 节点是 current 节点(rootFiber)的副本
- workInProgress 的 alternate 将指向 current
- current 的 alternate 反过来指向 workInProgress
完成之后会进入 workLoopSync
- 通过 while 反复判断 workInProgress 是否为空
- performUnitOfWork 触发对 beginWork 的调用,进而实现对新 Fiber 节点的创建
- beginWork 的入参是一对 alternate 连接起来的 workInprogress 和 current 节点
- beginWork 的核心逻辑是根据 fiber 节点(workInProgress)的 tag 属性的不同调用不同的节点创建函数
- 这些创建函数都会通过 reconcileChildren 方法,生成当前节点的子点
- reconcileChildeFibers 和 mounChildFibers 的不同,在于对副作用的处理不同
- ChildReconciler 中定义大量如 placeXXX、deleteXXX、updateXXX、reconcileXXX 函数
- 覆盖对 Fiber 节点的创建、增加、删除、修改,直接或间接被 reconcileChildFibers 所调用
- ChildReconciler 返回一个 reconcileChildFibers 的函数
- 逻辑分发器,根据入参不同,执行不同的 Fiber 节点操作,返回不同的目标 Fiber 节
- 这些创建函数都会通过 reconcileChildren 方法,生成当前节点的子点
不同的 Fiber 节点将通过 child、return、sibling 建立关系
- child、return 记录父子节点关系
- sibling 记录兄弟节点关系
completeWork 工作原理
工作内容:负责处理 Fiber 节点到 DOM 节点的映射逻辑
3 个关键动作:
- 创建 DOM 节点(CreateInstance)
- 将 DOM 节点插入到 DOM 树中(AppendAllChildren)
- 为 DOM 节点设置属性(FinalizeInitialChildren)
创建好的 DOM 节点会被赋值给 workInProgress 节点的 stateNode 属性
将 DOM 节点插入到 DOM 树的操作是通过 appendAllChildren 函数完成
实际上是将子 Fiber 节点所对应的 DOM 节点挂载其父 Fiber 节点所对应的 DOM 节点
completeUnitOfWork 开启收集 EffectList 大循环
针对传入的当前节点,调用 completeWork
将当前节点的副作用链插入到其父节点对应的副作用链中
以当前节点为起点,循环遍历其兄弟节点及其父节点render 阶段的工作目标
副作用链 effectList
render 阶段“工作成果”的一个集合
都是当前 Fiber 节点的后代节点
- 都有待处理的副作用
Fiber 节点的 effectList 里记录是其需要更新的后代节点
将当前节点的副作用链插入到其父节点对应的副作用链中
completeWork 是自底向上执行的,所以子节点的 completeWork 总是比父节点先执行
把所有需要更新的 Fiber 节点单独串成一串链表,方便后续有针对性地对进行更新,这就是“收集副作用”过程
- App FiberNode 的 flags 属性为 3,大于 PerformedWork
因些会进入 effectList 的创建逻辑 - 创建 effectList 时,并不是为当前 Fiber 节点创建,而为它的父节点创建
App 节点的父节点是 rootFiber,rootFiber 的 effectList 此时为空 - rootFiber 的 firstEffect 和 lastEffect 揭地都会指向 App 节点
App 节点由此成为 effectList 中唯一一个 FiberNode
commit 阶段
只负责实现更新,而不负责寻找更新
- before mutation 阶段 DOM 节点还没有被渲染到界面
- mutation 负责 DOM 节点的渲染
- layout 处理 DOM 渲染完毕之后的收尾逻辑
还会把 fiberRoot 的 current 指针指向 workInProgress Fiber 树