在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阶段前置工作

    1. 调用flushPassiveEffects执行完所有effect的工作
    2. 初始化相干变量
    3. 赋值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,而componentDidMountcomponentDidUpdate会在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 后

    1. 依据rootDoesHavePassiveEffects赋值相干变量
    2. 执行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
    该函数次要做了如下两件事

    1. 执行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,          );        }}
  1. 调度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次要做了如下几件事

    1. 调用commitDetachRef解绑ref(第11章hook会解说)
    2. 依据effectTag执行对应的dom操作
    3. 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次要做了

    1. 调用commitLayoutEffectOnFiber执行相干生命周期函数或者hook相干callback
    2. 执行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;          }        }      }