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;
}
}