Skip to content
目录

commitAllLifeCycles

commitAllLifeCycles阶段做的事情就比较简单

  • 调用useLayoutEffect的回调函数
  • 针对Class组件,分别调用componentDidMount或者componentDidUpdate
  • 在声明周期中产生的更新,比如setState(), 会提交当前的更新
  • 针对dom节点执行如focus()等动作
  • 处理ref的node节点赋值等

注意

这个阶段没有执行useEffect的回调

typescript
function commitAllLifeCycles(
  finishedRoot: FiberRoot,
  committedExpirationTime: ExpirationTime,
) {
  while (nextEffect !== null) {
    const effectTag = nextEffect.effectTag;
    // 具有回调以及更新的tag会调用commitLifeCycles
    if (effectTag & (Update | Callback)) {
      const current = nextEffect.alternate;
      commitLifeCycles(
        finishedRoot,
        current,
        nextEffect,
        committedExpirationTime,
      );
    }
    if (effectTag & Ref) {
      commitAttachRef(nextEffect);
    }
    nextEffect = nextEffect.nextEffect;
  }
}

commitLifeCycles

执行具体的

针对Function组件的处理

typescript
case SimpleMemoComponent: {
  // 传入UnmountLayout 和 MountLayout
  // useLayoutEffect具有 MountLayout的tag
  // 所以在这里 useLayoutEffect会被调用, 此时,DOM节点已经被真正的渲染到页面上
  commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
  break;
}

说明

此方法根据传入的tag不同,分别调用useEffect和useLayoutEffect; 但是在这里useLayoutEffect具有 MountLayout的tag,所以只会调用useLayoutEffect的回调

typescript
function commitHookEffectList(
  unmountTag: number,
  mountTag: number,
  finishedWork: Fiber,
) {
  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
  let lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
  if (lastEffect !== null) {
    const firstEffect = lastEffect.next;
    let effect = firstEffect;
    do {
      if ((effect.tag & unmountTag) !== NoHookEffect) {
        // 卸载
        const destroy = effect.destroy;
        effect.destroy = undefined;
        if (destroy !== undefined) {
          destroy();
        }
      }
      if ((effect.tag & mountTag) !== NoHookEffect) {
        // 挂载
        const create = effect.create;
        effect.destroy = create();
      }
      effect = effect.next;
    } while (effect !== firstEffect);
  }
}

针对Class组件的处理

首先,根据当前渲染,判断是调用componentDidMount或者说是componentDidUpdate;

其次,因为在这两个阶段是可以调用setState产生更新的,所以在调用完后,需要判断当前节点的 updateQueue, 如果存在更新,就提交当前的更新进行调度渲染

typescript
case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.effectTag & Update) {
        // 如果current是null,说明是第一次挂在 执行componentDidMount
        if (current === null) {
          instance.componentDidMount();
        } else {
          // 否则执行 componentDidUpdate
          const prevProps =
            finishedWork.elementType === finishedWork.type
              ? current.memoizedProps
              : resolveDefaultProps(finishedWork.type, current.memoizedProps);
          const prevState = current.memoizedState;
          instance.componentDidUpdate(
            prevProps,
            prevState,
            instance.__reactInternalSnapshotBeforeUpdate,
          );
        }
      }
      const updateQueue = finishedWork.updateQueue;
      // 如果updateQueue不等于null,说明在生命周期中产生了更新
      // 需要执行updateQueue
      if (updateQueue !== null) {
        commitUpdateQueue(
          finishedWork,
          updateQueue,
          instance,
          committedExpirationTime,
        );
      }
      return;
    }

注意

componentDidUpdate的第三个参数是在Snapshot阶段设置的,在这里读取出来,作为第三个参数传递

针对HostComponent的处理

针对DOM的处理,就比较简单了,主要是针对dom元素的focus进行处理

typescript
case HostComponent: {
  const instance: Instance = finishedWork.stateNode;
  if (current === null && finishedWork.effectTag & Update) {
    const type = finishedWork.type;
    const props = finishedWork.memoizedProps;
    // 执行host component的一些动作
    // 比如input的focus
    commitMount(instance, type, props, finishedWork);
  }

  return;
}
typescript
export function commitMount(
  domElement: Instance,
  type: string,
  newProps: Props,
  internalInstanceHandle: Object,
): void {
  if (shouldAutoFocusHostComponent(type, newProps)) {
    ((domElement: any):
      | HTMLButtonElement
      | HTMLInputElement
      | HTMLSelectElement
      | HTMLTextAreaElement).focus();
  }
}

生命周期阶段产生的更新的处理

不管是Function组件还是Class组件,更新都是通过setState产生的;

都会经历如下步骤:

  • 创建Update
  • 将当前Update添加到Fiber节点的UpdateQueue上
  • 执行 scheduleWork 方法

requestWork中,如果当前的isRendering是true,就直接return了

也就是说,在生命周期中产生的更新不会走到performWork;

那么这个更新是如果执行的呢?

在一开始render节点,其实执行了performWork,如下代码:

typescript
while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
      findHighestPriorityRoot();
    }

也就是说,在声明周期中产生的更新,会在这个while里面重新执行一次RenderRoot->CommitRoot阶段

commitUpdateQueue 具体做了什么?

这里做的事情主要是执行更新的回调callback

比如如下代码:

typescript
this.setState(newState, callback)
typescript
export function commitUpdateQueue<State>(
  finishedWork: Fiber,
  finishedQueue: UpdateQueue<State>,
  instance: any,
  renderExpirationTime: ExpirationTime,
): void {
  if (finishedQueue.firstCapturedUpdate !== null) {
    if (finishedQueue.lastUpdate !== null) {
      finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
      finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
    }
    finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
  }

  commitUpdateEffects(finishedQueue.firstEffect, instance);
  finishedQueue.firstEffect = finishedQueue.lastEffect = null;

  commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
  finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
}
typescript
function commitUpdateEffects<State>(
  effect: Update<State> | null,
  instance: any,
): void {
  while (effect !== null) {
    const callback = effect.callback;
    if (callback !== null) {
      effect.callback = null;
      callCallback(callback, instance);
    }
    effect = effect.nextEffect;
  }
}

备案号: 浙ICP备2023000081号