Skip to content
On this page

该阶段代码在 mutation 完成(DOM 修改完成)之后执行

注意:由于 JS 的同步执行阻塞了主线程,所以此时 JS 已经可以获取到新的DOM,但是浏览器对新的DOM并没有完成渲染。

该阶段触发的生命周期钩子和hook可以直接访问到已经改变后的DOM,即该阶段是可以参与DOM layout的阶段。

commitLayoutEffects

js
// ReactFiberWorkLoop.js
// commitRootImpl 函数内调用
function commitRootImpl() {
	// 上面是mutation 阶段
	// ...同样是通过遍历 effectList

	// 此时已经完成新 Fiber 树的渲染,修改FiberRootNode 的 current 指针,指向新的树
	root.current = finishedWork
	// layout 阶段 会调用 componentDidMount/ Update 生命周期,用到新的 Fiber
	nextEffect = firstEffect
	do {
		commitLayoutEffects(root, lanes);	
	} while (nextEffect !== null)
	nextEffect = null
}
// commitLayoutEffects
function commitLayoutEffects(root, lanes) {
	while (nextEffect !== null) {
		const flags = nextEffect.flags
		// 调用生命周期钩子和hook
		if (flags & (Update | Callback)) {
			const current = nextEffect.alternate;
			commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
	    }
	    // 赋值 ref
	    if (flags & Ref) {
		    commitAttachRef(nextEffect)
	    }
	    nextEffect = nextEffect.nextEffect
	}
}

类组件

js
// ReactFiberCommitWork.js
// 引入别名 commitLayoutEffectOnFiber
function commitLifeCycles(
	finishedRoot, current, finishedWork, committedLanes
) {
	switch (finishedWork.tag) {
		// 根据 Fiber的 tag 执行
		case ClassComponent: {
			// 类组件
			const instance = finishedWork.stateNode
			if (finishedWork.flags & Update) {
				// 触发 生命周期钩子
				if (current === null) {
					instance.componentDidMount();
				} else {
					instance.componentDidUpdate(
						prevProps,
						prevState, 
						instance.__reactInternalSnapshotBeforeUpdate
					);
				}
			}
			const updateQueue = finishedWork.updateQueue
			if (updateQueue !== null) {
				// setState 的 第二个参数(回调函数在此执行)
				commitUpdateQueue(finishedWork, updateQueue, instance);
			}
			return
		}
	}
}

函数组件

调用useLayoutEffect hook回调函数,调度useEffect销毁回调函数

js
function commitLifeCycles() {
	switch (finishedWork.tag) {
		// 函数组件相关
		case FunctionComponent:{
			// useLayoutHook 回调
			commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork)
			// useEffect 回调的调度
			schedulePassiveEffects(finishedWork);
			return
		}
	}
}

commitHookEffectListMount

js
// ReactFiberCommitWork.js
function commitHookEffectListMount(tag, finishedWork) {
	// tag:传入的HookLayout,表示执行的是 useLayoutHook
	// finishedWork:App Fiber节点
	const updateQueue = finishedWork.updateQueue
	const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null
	if (lastEffect !== null) {
		const firstEffect = lastEffect.next
		let effect = firstEffect
		do {
			if (effect.tag & tag) {
				const create = effect.create
				effect.destroy = create() 
				// layoutEffect 返回的回调函数,作为destroy
				// destroy 在 mutation 阶段先调用,然后在 layout 阶段调用 create
				// destroy 和 create 都是同步调用
			}
		} while(effect !== firstEffect)
	}
}

schedulePassiveEffects

js
function schedulePassiveEffects(finishedWork) {
	const updateQueue = (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
	      ) {
		      // 推进调度队列中
	        enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
	        enqueuePendingPassiveHookEffectMount(finishedWork, effect);
	      }
	      effect = next;
	    } while (effect !== firstEffect);
	  }
	}

function enqueuePendingPassiveHookEffectUnmount(fiber, effect) {
	pendingPassiveHookEffectsUnmount.push(effect, fiber)
	// rootDoesHavePassiveEffects 用于标记本次更新中存在useEffect 回调
	if (!rootDoesHavePassiveEffects) {
		rootDoesHavePassiveEffects = true
		scheduleCallback(NormalSchedulerPriority, () => {
			// 调度回调函数
			// 内部主要是遍历 之前的 effect 销毁(destroy)和 新的 create 回调
			flushPassiveEffects();
			return null;
		})
	}
}



执行完 commitLayoutEffects

js
function commitRootImpl() {
	//...commitLayoutEffects(root, lanes);
	nextEffect = null;
	// 至此 commit 三个子阶段完成
	// 上面推入调度队列过程中赋值为 true
	if (rootDoesHavePassiveEffects) {
		rootDoesHavePassiveEffects = false;
		rootWithPendingPassiveEffects = root;
	}
}

flushPassiveEffects

js
// 内部调用
function flushPassiveEffectsImpl(){
	// 该函数执行了 useEffect 的 destroy 回调 和 本次更新的 create 回调
	// 上面赋值了为 root
	if (rootWithPendingPassiveEffects === null) {return false;}
	const unmountEffects = pendingPassiveHookEffectsUnmount;
	// 遍历执行 unmountEffects 中的 destroy
	const mountEffects = pendingPassiveHookEffectsMount;
	// 遍历执行 create
}

注意点

useLayoutEffect在commit阶段中同步执行 的,如果在其 create 回调中产生新的 setState ,由于同步执行(第一次 setState,回调,回调中再次 setState,会再次执行 useLayout 回调)不会在屏幕上显示 中间值

useEffectcommit 阶段之后异步执行,是可能会显示