render阶段开始于performSyncWorkOnRoot或performConcurrntWorkOnRoot方法的调用。
两个的区别是是否调用了shouldYield。如果浏览器没有剩余时间shouldYield会终止循环,直到浏览器有空闲时间后再继续遍历。
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
// workInProgress代表当前以创建的workInProgress fiber
// performUnitOfWork方法会创建下一个fiber并赋值给workInProgress,并将workInProgress与已经创建的fiber节点连接起来构成Fiber树
节点遍历的方式实现可中断的递归
- 递 beginWork
从rootFiber开始深度优先遍历。为遍历到的每个fiber节点调用beginWork
beginWork的执行流程
function beginWork(
current: Fiber | null,// 当前组件对应的fiber节点在上一次更新时的fiber节点:workInProgress.alternate
workInProgress: Fiber,// 当前组件对应的fiber节点
renderLanes: Lanes,// 优先级相关
): Fiber | null {
if(current!==null){
// update阶段,根据diff复用fiber节点
}else{
// mount阶段,fiber.tag的不同,创建不同类型的子fiber
}
}
如果在组件的mount阶段,是首次渲染,此时current==null,update阶段current!==null(后续将current==null作为判断组件是mount还是update阶段的依据
)。beginWork可以分为两部分,update时,在满足一定条件下可以复用current节点(diff
),mount时,根据fiber.tag的不同,创建不同类型的子fiber。
diff阶段要知道增删改查的dom操作,具体的操作类型保存在fiber的effectTag中(placement\update\placementAndUpdate\deletion)
- 归 completeWork
当某个fiber节点执行完completeWork,如果其存在兄弟fiber节点,会进入兄弟fiber的“递”阶段,如果不存在兄弟节点就会进入父fiber的“归”阶段。
completeWork的执行流程
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
case ClassComponent: {
// ...省略
return null;
}
case HostRoot: {
// ...省略
updateHostContainer(workInProgress);
return null;
}
case HostComponent: {
// ...省略
return null;
}
“归”阶段和beginwork一样根据current===null判断是mount还是update,根据不同的workInProgress.tag作出不同的处理。
update时,fiber已经存在dom节点,不需要生成dom,需要做的是处理props,如事件、样式等。被处理完的props会被赋值给workInProgress.updateQueue,并最终在commit阶段被渲染到页面上。mount时,会为fiber节点生成对应的dom节点,将子孙的dom节点插入到刚生成的dom节点中,和与update阶段类似的处理props逻辑。
- effectList
在completeWork的上层函数completeUnitOfWork中,每个执行完completeWork且存在effectTag的fiber节点会被保存在effectList单向链表中。
effectList中第一个fiber节点保存在fiber.firstEffect,最后一个元素保存在lastEffect中。
nextEffect nextEffect
rootFiber.firstEffect------------------->fiber------------------->fiber
// 这样只要在commit阶段遍历effectList就能执行所有effect了
“递”和“归”阶段交错之心直到“归”到rootFiber,render的工作就结束了。在performSyncWorkOnRoot函数中fiberRootNood被传递给commitRoot,开启commit阶段流程。
网友评论