Skip to content
On this page

该阶段执行 commitBeforeMutationEffects 函数

完成三件事

  1. 针对 DOM 节点 的focus 和 blur 状态进行操作(不关注)
  2. 执行 commitBeforeMutationEffectOnFiber
  3. 调用 useEffect 回调

commitBeforeMutationEffects

js
function commitBeforeMutationEffects() {
  while (nextEffect !== null) {
    const current = nextEffect.alternate;

    if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
      /// 处理 focus/blur 状态
    }
		// 根据之前生成的 effectList 进行处理
    const flags = nextEffect.flags;
    if ((flags & Snapshot) !== NoFlags)
	    // 内部调用`getSnapshotBeforeUpdate`生命周期钩子
      commitBeforeMutationEffectOnFiber(current, nextEffect);
    }
    // 如果存在 useEffect 依赖发生更改的 对应的tag,调度 useffect 回调
    if ((flags & Passive) !== NoFlags) {
      if (!rootDoesHavePassiveEffects) {
        rootDoesHavePassiveEffects = true;
        // 调度 useEffect 的 hook 执行
        scheduleCallback(NormalSchedulerPriority, () => {
	        // 在commit阶段之后 触发 useEffect
		      flushPassiveEffects();
          return null;
        });
      }
    }
    // commit 阶段是同步执行的,useEffect 以 Normal 优先级进行调度
    nextEffect = nextEffect.nextEffect;
  }
}

commitBeforeMutationEffectOnFiber

js
function commitBeforeMutationLifeCycles(current, finishedWork) {
	// 引入别名commitBeforeMutationEffectOnFiber
	switch (finishedWork.tag) {
	 // 类组件
		case ClassComponent: {
			if (finishedWork.flags & Snapshot) {
				if (current !== null) {
          const prevProps = current.memoizedProps;
          const prevState = current.memoizedState;
					// ReactComponent实例
          const instance = finishedWork.stateNode;
          // 调用实例的 getSnapshotBeforeUpdate
          // 此时页面还没有更新
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );        
        }	
			}
    }
	}
}

useEffect 如何异步调度

  1. before mutation阶段scheduleCallback中调度flushPassiveEffects(未调用)
  2. layout阶段之后将effectList赋值给rootWithPendingPassiveEffects
  3. scheduleCallback触发flushPassiveEffectsflushPassiveEffects内部遍历rootWithPendingPassiveEffects

为什么需要异步调用

执行时机:

componentDidMountcomponentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。

可见,useEffect 异步执行的原因主要是,防止同步执行时,阻塞浏览器渲染