Appearance
commitLayoutEffects
Commit 阶段对于副作用的第三次遍历处理过程。
在这个阶段主要就是执行 DOM 插入完成后的收尾工作。
主要做两件事
- 对于不同类型的节点,执行不同的 commit 方法
- 对于函数组件, 执行 useLayoutEffect 中的 create 方法 并将 create 方法的返回值赋值给 effect 的 destroy 属性
- 对于类组件,
- 初次渲染: 执行
componentDidMount生命周期方法 - 更新类型: 执行
componentDidUpdate生命周期方法 - 执行 所有 setState 的 callback 方法, 并清空
update.effects
- 初次渲染: 执行
- 对于 DOM 节点
- 执行 特地 DOM 节点的 focus 方法
- 对于根节点 HostRoot
- 执行 所有 setState 的 callback 方法, 并清空
update.effects
- 执行 所有 setState 的 callback 方法, 并清空
- 对于 Profiler 组件
- 执行 onRender 方法
- 更新最终的 ref 的值
源码讲解
js
/**
* mounted 后的操作
* 1. 通过 commitLifeCycles
* - 函数组件 useLayoutEffect create函数回调
* - class组件 初次渲染 - componentDidMount 生命周期
* 更新渲染 - componentDidUpdate 生命周期
* 2. ref.current 的更新
* @param {*} root
* @param {*} committedLanes
*/
function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
// TODO: Should probably move the bulk of this function to commitWork.
while (nextEffect !== null) {
setCurrentDebugFiberInDEV(nextEffect);
const flags = nextEffect.flags;
// 带有更新或者回调标记的副作用执行( commitLifeCycles as commitLayoutEffectOnFiber)
if (flags & (Update | Callback)) {
const current = nextEffect.alternate;
commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
}
if (enableScopeAPI) {
if (flags & Ref && nextEffect.tag !== ScopeComponent) {
commitAttachRef(nextEffect);
}
} else {
if (flags & Ref) {
commitAttachRef(nextEffect);
}
}
resetCurrentDebugFiberInDEV();
nextEffect = nextEffect.nextEffect;
}
}FunctionComponent
函数类型的组件:执行 useLayoutEffect 中的 create 方法 并将 create 方法的返回值赋值给 effect 的 destroy 属性
js
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableSuspenseLayoutEffectSemantics || !offscreenSubtreeWasHidden) {
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
finishedWork.mode & ProfileMode
) {
try {
startLayoutEffectTimer();
// 执行 useLayoutEffect 中的 create 方法
// 并将 create 方法的返回值赋值给 effect 的 destroy 属性
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
} finally {
recordLayoutEffectDuration(finishedWork);
}
} else {
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
}
}
break;
}
}ClassComponent
- 初始渲染 - componentDidMount 生命周期
- 更新渲染 - componentDidUpdate 生命周期
- commitUpdateQueue(finishedWork, updateQueue, instance); 执行 effect 的 callback
js
case ClassComponent : {
// Class 组件的实例对象
const instance = finishedWork.stateNode;
if (finishedWork.flags & Update) {
if (!offscreenSubtreeWasHidden) {
// 初次渲染的组件 执行 componentDidMount 生命周期方法
if (current === null) {
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
finishedWork.mode & ProfileMode
) {
try {
startLayoutEffectTimer();
instance.componentDidMount();
} finally {
recordLayoutEffectDuration(finishedWork);
}
} else {
instance.componentDidMount();
}
} else {
// 对于更新的组件, 执行 componentDidUpdate 生命周期方法
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
const prevState = current.memoizedState;
}
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
finishedWork.mode & ProfileMode
) {
try {
startLayoutEffectTimer();
// 执行 componentDidUpdate 生命周期方法
// 并将 getSnapshotBeforeUpdate 方法的返回值赋值给 instance.__reactInternalSnapshotBeforeUpdate 属性
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate
);
} finally {
recordLayoutEffectDuration(finishedWork);
}
} else {
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate
);
}
}
}
// 处理所有 update的 callback 和 清空 update.effects
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitUpdateQueue(finishedWork, updateQueue, instance);
}
}HostComponent 元素节点类型
- 初次渲染的时候,如果存在 autoFocus 执行 focus() 聚焦方法
js
switch (finishedWork.tag) {
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
// 新的节点,且存在更新类型的副作用
if (current === null && finishedWork.flags & Update) {
const type = finishedWork.type;
const props = finishedWork.memoizedProps;
commitMount(instance, type, props, finishedWork);
}
return;
}
}
/**
* autoFocus 属性 最后触发元素的 focus() 方法
* @param {*} domElement
* @param {*} type
* @param {*} newProps
* @param {*} internalInstanceHandle
*/
export function commitMount(
domElement: Instance,
type: string,
newProps: Props,
internalInstanceHandle: Object
): void {
if (shouldAutoFocusHostComponent(type, newProps)) {
((domElement: any):
| HTMLButtonElement
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement).focus();
}
}