commitRoot
在RenderRoot阶段,Fiber树已经构建好了,并且在更新阶段,需要插入、删除、更新的DOM节点的effectTag都标记在了Fiber节点上;
在CommitRoot阶段,就是来处理这些具体的操作;在Concurrent Mode下,具有一些高优先级的任务也是在这个阶段被处理;
- 在
reconciler阶段,针对有副作用的节点,将当前的具有副作用的Fiber添加到FirstEffect->LastEffect链上 - 在 completeUnitOfWork阶段,将FirstEffect->LastEffect依次往父节点上移动并连接
renderRoot阶段结束后,将当前处理的Fiber(RootFiber)的alternate指针赋值给FiberRoot的finishedWork
typescript
function onComplete(
root: FiberRoot,
finishedWork: Fiber,
expirationTime: ExpirationTime,
) {
root.pendingCommitExpirationTime = expirationTime;
// finishedWork 赋值
root.finishedWork = finishedWork;
}
// root.current.alternate 其实就是RootFiber在运行中更新的那个副本
const rootWorkInProgress = root.current.alternate;
onComplete(root, rootWorkInProgress, expirationTime);总结
在整个commitRoot阶段都是围绕着finishedWork进行遍历处理
做了什么?
- 提交Snapshot
- 提交HostComponent的 side effect, 渲染到页面上
- 提交所有组件的生命周期
伪代码实现
commitRoot代码比较长,这里只看重要的部分,首先,在内部是循环finishedWork.firstEffect链,一共有三次循环。那么每次循环都做了什么呢?
- 第一个循环: 主要处理了Snapshot相关的事情,比如class组件的getSnapshotBeforeUpdate方法的调用
- 第二个循环: 将dom节点进行插入、更新、删除、然后append到container中;
- 第三个循环: 调用class组件相关的声明周期,函数组件的useLayoutEffect声明周期、以及useLayoutEffect和useEffect的卸载函数;
- 循环走完后,会将useEffect使用Scheduler根据优先级调度,也就是进行(Concurrent Mode下)异步更新
typescript
function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
isWorking = true;
isCommitting = true;
let nextEffect
let firstEffect = finishedWork.firstEffect;
nextEffect = firstEffect;
// 对一个循环
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitBeforeMutationLifecycles();
} catch (e) {
didError = true;
}
}
nextEffect = firstEffect;
// 第二个循环
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitAllHostEffects();
} catch (e) {
didError = true;
error = e;
}
}
nextEffect = firstEffect;
// 第三个循环
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitAllLifeCycles(root, committedExpirationTime);
} catch (e) {
didError = true;
error = e;
}
}
if (firstEffect !== null && rootWithPendingPassiveEffects !== null) {
// 这个时候调用useEffect,先调用unmount, 在调用 mount ,这个时候已经渲染完成了
// 不会阻塞渲染
let callback = commitPassiveEffects.bind(null, root, firstEffect);
passiveEffectCallbackHandle = runWithPriority(NormalPriority, () => {
return schedulePassiveEffects(callback);
});
passiveEffectCallback = callback;
}
isCommitting = false;
isWorking = false;
onCommitRoot(finishedWork.stateNode);
}