在render阶段的开端会调用commitRoot(root);进入commit阶段,这里的root指的就是fiberRoot,而后会遍历render阶段生成的effectList,effectList上的Fiber节点保留着对应的props变动。之后会遍历effectList进行对应的dom操作和生命周期、hooks回调或销毁函数,各个函数做的事件如下
在commitRoot函数中其实是调度了commitRootImpl函数
//ReactFiberWorkLoop.old.jsfunction commitRoot(root) { var renderPriorityLevel = getCurrentPriorityLevel(); runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel)); return null;}
在commitRootImpl的函数中次要分三个局部:
commit阶段前置工作
- 调用flushPassiveEffects执行完所有effect的工作
- 初始化相干变量
- 赋值firstEffect给前面遍历effectList用
//ReactFiberWorkLoop.old.jsdo { // 调用flushPassiveEffects执行完所有effect的工作 flushPassiveEffects(); } while (rootWithPendingPassiveEffects !== null); //... // 重置变量 finishedWork指rooFiber root.finishedWork = null; //重置优先级 root.finishedLanes = NoLanes; // Scheduler回调函数重置 root.callbackNode = null; root.callbackId = NoLanes; // 重置全局变量 if (root === workInProgressRoot) { workInProgressRoot = null; workInProgress = null; workInProgressRootRenderLanes = NoLanes; } else { } //rootFiber可能会有新的副作用 将它也退出到effectLis let firstEffect; if (finishedWork.effectTag > PerformedWork) { if (finishedWork.lastEffect !== null) { finishedWork.lastEffect.nextEffect = finishedWork; firstEffect = finishedWork.firstEffect; } else { firstEffect = finishedWork; } } else { firstEffect = finishedWork.firstEffect; }
mutation阶段
遍历effectList别离执行三个办法commitBeforeMutationEffects、commitMutationEffects、commitLayoutEffects执行对应的dom操作和生命周期
在介绍双缓存Fiber树的时候,咱们在构建完workInProgress Fiber树之后会将fiberRoot的current指向workInProgress Fiber,让workInProgress Fiber成为current,这个步骤产生在commitMutationEffects函数执行之后,commitLayoutEffects之前,因为componentWillUnmount产生在commitMutationEffects函数中,这时还能够获取之前的Update,而componentDidMount
和
componentDidUpdate会在commitLayoutEffects中执行,这时曾经能够获取更新后的实在dom了
function commitRootImpl(root, renderPriorityLevel) { //... do { //... commitBeforeMutationEffects(); } while (nextEffect !== null); do { //... commitMutationEffects(root, renderPriorityLevel);//commitMutationEffects } while (nextEffect !== null); root.current = finishedWork;//切换current Fiber树 do { //... commitLayoutEffects(root, lanes);//commitLayoutEffects } while (nextEffect !== null); //...}
mutation 后
- 依据rootDoesHavePassiveEffects赋值相干变量
- 执行flushSyncCallbackQueue解决componentDidMount等生命周期或者useLayoutEffect等同步工作
const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;// 依据rootDoesHavePassiveEffects赋值相干变量if (rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = false; rootWithPendingPassiveEffects = root; pendingPassiveEffectsLanes = lanes; pendingPassiveEffectsRenderPriority = renderPriorityLevel;} else {}//...// 确保被调度ensureRootIsScheduled(root, now());// ...// 执行flushSyncCallbackQueue解决componentDidMount等生命周期或者useLayoutEffect等同步工作flushSyncCallbackQueue();return null;
当初让咱们来看看mutation阶段的三个函数别离做了什么事件
commitBeforeMutationEffects
该函数次要做了如下两件事执行getSnapshotBeforeUpdate
在源码中commitBeforeMutationEffectOnFiber对应的函数是commitBeforeMutationLifeCycles在该函数中会调用getSnapshotBeforeUpdate,当初咱们晓得了getSnapshotBeforeUpdate是在mutation阶段中的commitBeforeMutationEffect函数中执行的,而commit阶段是同步的,所以getSnapshotBeforeUpdate也同步执行
function commitBeforeMutationLifeCycles( current: Fiber | null, finishedWork: Fiber,): void { switch (finishedWork.tag) { //... case ClassComponent: { if const instance = finishedWork.stateNode; const snapshot = instance.getSnapshotBeforeUpdate(//getSnapshotBeforeUpdate finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState, ); }}
- 调度useEffect
在flushPassiveEffects函数中调用flushPassiveEffectsImpl遍历pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount,执行对应的effect回调和销毁函数,而这两个数组是在commitLayoutEffects函数中赋值的(待会就会讲到),mutation后effectList赋值给rootWithPendingPassiveEffects,而后scheduleCallback调度执行flushPassiveEffects
相干参考视频解说:进入学习
function flushPassiveEffectsImpl() { if (rootWithPendingPassiveEffects === null) {//在mutation后变成了root return false; } const unmountEffects = pendingPassiveHookEffectsUnmount; pendingPassiveHookEffectsUnmount = [];//useEffect的回调函数 for (let i = 0; i < unmountEffects.length; i += 2) { const effect = ((unmountEffects[i]: any): HookEffect); //... const destroy = effect.destroy; destroy(); } const mountEffects = pendingPassiveHookEffectsMount;//useEffect的销毁函数 pendingPassiveHookEffectsMount = []; for (let i = 0; i < mountEffects.length; i += 2) { const effect = ((unmountEffects[i]: any): HookEffect); //... const create = effect.create; effect.destroy = create(); }}
componentDidUpdate或componentDidMount会在commit阶段同步执行(这个前面会讲到),而useEffect会在commit阶段异步调度,所以实用于数据申请等副作用的解决 > 留神,和在render阶段的fiber node会打上Placement等标签一样,useEffect或useLayoutEffect也有对应的effect Tag,在源码中对应export const Passive = /* */ 0b0000000001000000000;
function commitBeforeMutationEffects() { while (nextEffect !== null) { const current = nextEffect.alternate; const effectTag = nextEffect.effectTag; // 在commitBeforeMutationEffectOnFiber函数中会执行getSnapshotBeforeUpdate if ((effectTag & Snapshot) !== NoEffect) { commitBeforeMutationEffectOnFiber(current, nextEffect); } // scheduleCallback调度useEffect if ((effectTag & Passive) !== NoEffect) { if (!rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = true; scheduleCallback(NormalSchedulerPriority, () => { flushPassiveEffects(); return null; }); } } nextEffect = nextEffect.nextEffect;//遍历effectList }}
commitMutationEffects commitMutationEffects次要做了如下几件事
- 调用commitDetachRef解绑ref(第11章hook会解说)
- 依据effectTag执行对应的dom操作
- useLayoutEffect销毁函数在UpdateTag时执行
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) { //遍历effectList while (nextEffect !== null) { const effectTag = nextEffect.effectTag; // 调用commitDetachRef解绑ref if (effectTag & Ref) { const current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } // 依据effectTag执行对应的dom操作 const primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating); switch (primaryEffectTag) { // 插入dom case Placement: { commitPlacement(nextEffect); nextEffect.effectTag &= ~Placement; break; } // 插入更新dom case PlacementAndUpdate: { // 插入 commitPlacement(nextEffect); nextEffect.effectTag &= ~Placement; // 更新 const current = nextEffect.alternate; commitWork(current, nextEffect); break; } //... // 更新dom case Update: { const current = nextEffect.alternate; commitWork(current, nextEffect); break; } // 删除dom case Deletion: { commitDeletion(root, nextEffect, renderPriorityLevel); break; } } nextEffect = nextEffect.nextEffect; } }
当初让咱们来看看操作dom的这几个函数 **commitPlacement插入节点:** 简化后的代码很清晰,找到该节点最近的parent节点和兄弟节点,而后依据isContainer来判断是插入到兄弟节点前还是append到parent节点后
function commitPlacement(finishedWork: Fiber): void { //... const parentFiber = getHostParentFiber(finishedWork);//找到最近的parent let parent; let isContainer; const parentStateNode = parentFiber.stateNode; switch (parentFiber.tag) { case HostComponent: parent = parentStateNode; isContainer = false; break; //... } const before = getHostSibling(finishedWork);//找兄弟节点 if (isContainer) { insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent); } else { insertOrAppendPlacementNode(finishedWork, before, parent); } }
**commitWork更新节点:** 在简化后的源码中能够看到 如果fiber的tag是SimpleMemoComponent会调用commitHookEffectListUnmount执行对应的hook的销毁函数,能够看到传入的参数是HookLayout | HookHasEffect,也就是说执行useLayoutEffect的销毁函数。 如果是HostComponent,那么调用commitUpdate,commitUpdate最初会调用updateDOMProperties解决对应Update的dom操作
function commitWork(current: Fiber | null, finishedWork: Fiber): void { if (!supportsMutation) { switch (finishedWork.tag) { //... case SimpleMemoComponent: { commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork); } //... } } switch (finishedWork.tag) { //... case HostComponent: { //... commitUpdate( instance, updatePayload, type, oldProps, newProps, finishedWork, ); } return; } }
function updateDOMProperties( domElement: Element, updatePayload: Array<any>, wasCustomComponentTag: boolean, isCustomComponentTag: boolean, ): void { // TODO: Handle wasCustomComponentTag for (let i = 0; i < updatePayload.length; i += 2) { const propKey = updatePayload[i]; const propValue = updatePayload[i + 1]; if (propKey === STYLE) { setValueForStyles(domElement, propValue); } else if (propKey === DANGEROUSLY_SET_INNER_HTML) { setInnerHTML(domElement, propValue); } else if (propKey === CHILDREN) { setTextContent(domElement, propValue); } else { setValueForProperty(domElement, propKey, propValue, isCustomComponentTag); } } }
**commitDeletion删除节点:** 如果是ClassComponent会执行componentWillUnmount,删除fiber,如果是FunctionComponent 会删除ref、并执行useEffect的销毁函数,具体可在源码中查看unmountHostComponents、commitNestedUnmounts、detachFiberMutation这几个函数
function commitDeletion( finishedRoot: FiberRoot, current: Fiber, renderPriorityLevel: ReactPriorityLevel, ): void { if (supportsMutation) { // Recursively delete all host nodes from the parent. // Detach refs and call componentWillUnmount() on the whole subtree. unmountHostComponents(finishedRoot, current, renderPriorityLevel); } else { // Detach refs and call componentWillUnmount() on the whole subtree. commitNestedUnmounts(finishedRoot, current, renderPriorityLevel); } const alternate = current.alternate; detachFiberMutation(current); if (alternate !== null) { detachFiberMutation(alternate); } }
commitLayoutEffects 在commitMutationEffects之后所有的dom操作都曾经实现,能够拜访dom了,commitLayoutEffects次要做了
- 调用commitLayoutEffectOnFiber执行相干生命周期函数或者hook相干callback
- 执行commitAttachRef为ref赋值
function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) { while (nextEffect !== null) { const effectTag = nextEffect.effectTag; // 调用commitLayoutEffectOnFiber执行生命周期和hook if (effectTag & (Update | Callback)) { const current = nextEffect.alternate; commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes); } // ref赋值 if (effectTag & Ref) { commitAttachRef(nextEffect); } nextEffect = nextEffect.nextEffect; } }
**commitLayoutEffectOnFiber:** 在源码中commitLayoutEffectOnFiber函数的别名是commitLifeCycles,在简化后的代码中能够看到,commitLifeCycles会判断fiber的类型,SimpleMemoComponent会执行useLayoutEffect的回调,而后调度useEffect,ClassComponent会执行componentDidMount或者componentDidUpdate,this.setState第二个参数也会执行,HostRoot会执行ReactDOM.render函数的第三个参数,例如
ReactDOM.render(<App />, document.querySelector("#root"), function() { console.log("root mount"); });
当初能够晓得useLayoutEffect是在commit阶段同步执行,useEffect会在commit阶段异步调度
function commitLifeCycles( finishedRoot: FiberRoot, current: Fiber | null, finishedWork: Fiber, committedLanes: Lanes, ): void { switch (finishedWork.tag) { case SimpleMemoComponent: { // 此函数会调用useLayoutEffect的回调 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork); // 向pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中push effect // 并且调度它们 schedulePassiveEffects(finishedWork); } case ClassComponent: { //条件判断... instance.componentDidMount(); //条件判断... instance.componentDidUpdate(//update 在layout期间同步执行 prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate, ); } case HostRoot: { commitUpdateQueue(finishedWork, updateQueue, instance);//render第三个参数 } } }
在schedulePassiveEffects中会将useEffect的销毁和回调函数push到pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中
function schedulePassiveEffects(finishedWork: Fiber) { const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any); const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; if (lastEffect !== null) { const firstEffect = lastEffect.next; let effect = firstEffect; do { const {next, tag} = effect; if ( (tag & HookPassive) !== NoHookEffect && (tag & HookHasEffect) !== NoHookEffect ) { //push useEffect的销毁函数并且退出调度 enqueuePendingPassiveHookEffectUnmount(finishedWork, effect); //push useEffect的回调函数并且退出调度 enqueuePendingPassiveHookEffectMount(finishedWork, effect); } effect = next; } while (effect !== firstEffect); } }
**commitAttachRef:** commitAttacRef中会判断ref的类型,执行ref或者给ref.current赋值
function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { const instance = finishedWork.stateNode; let instanceToUse; switch (finishedWork.tag) { case HostComponent: instanceToUse = getPublicInstance(instance); break; default: instanceToUse = instance; } if (typeof ref === "function") { // 执行ref回调 ref(instanceToUse); } else { // 如果是值的类型则赋值给ref.current ref.current = instanceToUse; } } }