该阶段触发的生命周期钩子和hook
可以直接访问到已经改变后的DOM
,即该阶段是可以参与DOM layout
的阶段。
与前两个阶段类似,layout阶段
也是遍历effectList
,执行函数。
具体执行的函数是commitLayoutEffects
。
commitLayoutEffects
代码如下:
function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
// 调用生命周期钩子和hook
if (effectTag & (Update | Callback)) {
const current = nextEffect.alternate;
commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
}
// 赋值ref
if (effectTag & Ref) {
commitAttachRef(nextEffect);
}
}
}
commitLayoutEffects
一共做了两件事:
commitLayoutEffectOnFiber(调用
生命周期钩子
和hook
相关操作)commitAttachRef(赋值 ref)
你可以在看到
commitLayoutEffectOnFiber
源码(commitLayoutEffectOnFiber
为别名,方法原名为commitLifeCycles
)
- 对于
ClassComponent
,他会通过current === null?
区分是mount
还是update
,调用componentDidMount
(opens new window)或。
触发状态更新
的this.setState
如果赋值了第二个参数回调函数
,也会在此时调用。
- 对于及相关类型,他会调用
useLayoutEffect hook
的回调函数
,调度useEffect
的销毁
与回调
函数
switch (finishedWork.tag) {
// 以下都是FunctionComponent及相关类型
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
// 执行useLayoutEffect的回调函数
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
// 调度useEffect的销毁函数与回调函数
schedulePassiveEffects(finishedWork);
return;
}
你可以从这里 (opens new window)看到这段代码
在上一节介绍时介绍过,mutation阶段
会执行useLayoutEffect hook
的销毁函数
。
结合这里我们可以发现,useLayoutEffect hook
从上一次更新的销毁函数
调用到本次更新的回调函数
调用是同步执行的。
而useEffect
则需要先调度,在Layout阶段
完成后再异步执行。
这就是useLayoutEffect
与useEffect
的区别。
- 对于
HostRoot
,即rootFiber
,如果赋值了第三个参数回调函数
,也会在此时调用。
commitAttachRef
function commitAttachRef(finishedWork: Fiber) {
if (ref !== null) {
const instance = finishedWork.stateNode;
// 获取DOM实例
switch (finishedWork.tag) {
case HostComponent:
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
if (typeof ref === "function") {
// 如果ref是函数形式,调用回调函数
ref(instanceToUse);
} else {
// 如果ref是ref实例形式,赋值ref.current
ref.current = instanceToUse;
}
}
代码逻辑很简单:获取DOM
实例,更新ref
。
至此,整个layout阶段
就结束了。
在结束本节的学习前,我们关注下这行代码:
你可以在这里 (opens new window)看到这行代码
在我们介绍过,workInProgress Fiber树
在commit阶段
完成渲染后会变为current Fiber树
。这行代码的作用就是切换fiberRootNode
指向的current Fiber树
。
那么这行代码为什么在这里呢?(在mutation阶段
结束后,layout阶段
开始前。)
我们知道componentWillUnmount
会在mutation阶段
执行。此时current Fiber树
还指向前一次更新的Fiber树
,在生命周期钩子内获取的DOM
还是更新前的。
总结
从这节我们学到,layout阶段
会遍历effectList
,依次执行commitLayoutEffects
。该方法的主要工作为“根据effectTag
调用不同的处理函数处理Fiber
并更新ref
。