render 阶段流程
render 阶段流程
总览
- 同异步更新调用不同的方法,
performSyncWorkOnRoot
或performConcurrentWorkOnRoot
,他们唯一的区别是是否调用shouldYield
shouldYield
终止循环、取决于浏览器帧剩余时间- Fiber Reconciler是从Stack Reconciler重构而来,通过遍历的方式实现可中断的递归,所以performUnitOfWork的工作可以分为递(beginWork)、归(completeWork)
递归遍历树
“递”阶段
首先从rootFiber
开始向下深度优先遍历。为遍历到的每个Fiber节点
调用beginWork方法。
该方法会根据传入的Fiber节点
创建子Fiber节点
,并将这两个Fiber节点
连接起来。
当遍历到叶子节点(即没有子组件的组件)时就会进入“归”阶段。
“归”阶段
在“归”阶段会调用completeWork处理Fiber节点
。
当某个Fiber节点
执行完completeWork
,如果其存在兄弟Fiber节点
(即fiber.sibling !== null
),会进入其兄弟Fiber
的“递”阶段。
注意多子树结构、存储的是第一个子节点和第一个(右)兄弟节点
// 指向父级Fiber节点 this.return = null; // 指向子Fiber节点 this.child = null; // 指向右边第一个兄弟Fiber节点 this.sibling = null;
如果不存在兄弟Fiber
,会进入父级Fiber
的“归”阶段。
“递”和“归”阶段会交错执行直到“归”到rootFiber
。至此,render阶段
的工作就结束了。
例子
function App() {
return (
<div>
i am
<span>KaSong</span>
</div>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
render 依次执行
1. rootFiber beginWork
2. App Fiber beginWork
3. div Fiber beginWork
4. "i am" Fiber beginWork
5. "i am" Fiber completeWork
6. span Fiber beginWork
7. span Fiber completeWork
8. div Fiber completeWork
9. App Fiber completeWork
10. rootFiber completeWork
之所以没有 “KaSong” Fiber 的 beginWork/completeWork,是因为作为一种性能优化手段,针对只有单一文本子节点的
Fiber
,React
会特殊处理。
beginWork()
beginWork
的工作是传入当前Fiber节点
,创建子Fiber节点
,我们从传参来看看具体是如何做的。
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// ...省略函数体
}
其中传参:
- current:当前组件对应的
Fiber节点
在上一次更新时的Fiber节点
,即workInProgress.alternate
- workInProgress:当前组件对应的
Fiber节点
- renderLanes:优先级相关,在讲解
Scheduler
时再讲解
除rootFiber
以外, 组件mount
时,由于是首次渲染,是不存在当前组件对应的Fiber节点
在上一次更新时的Fiber节点
,即mount
时current === null
。
组件update
时,由于之前已经mount
过,所以current !== null
。
所以我们可以通过current === null ?
来区分组件是处于mount
还是update
。
根据current 区分执行阶段
update
时:如果current
存在,在满足一定条件时可以复用current
节点,这样就能克隆current.child
作为workInProgress.child
,而不需要新建workInProgress.child
。mount
时:除fiberRootNode
以外,current === null
。会根据fiber.tag
不同,创建不同类型的子Fiber节点
completeWork()
类似beginWork
,completeWork
也是针对不同fiber.tag
调用不同的处理逻辑。
流程结尾
至此,render阶段
全部工作完成。在performSyncWorkOnRoot
函数中fiberRootNode
被传递给commitRoot
方法,开启commit阶段
工作流程。
commitRoot(root);