这次是接着上一期 render 阶段的文章,解析挂载时 render 阶段的 ” 归 ” 阶段 —-completeWork 函数
completeWork 开始阶段
在 performUnitOfWork 中执行完 beginWork,就会进入上面判断
if (next === null) {
// workInProgress 曾经不存在子树,就开始进行 "归" 阶段
completeUnitOfWork(unitOfWork);
} else {
// next 是 beginWork 调用后的返回值 workInProgress.child
workInProgress = next;
}
completeUnitOfWork
次要做了两件事,执行 completeWork 和收拢 EffectList
function completeUnitOfWork(unitOfWork: Fiber): void {
// 尝试实现以后的工作单元,而后挪动到下一个兄弟。如果没有更多的兄弟,返回到父 fiber。let completedWork = unitOfWork;
// 直到父节点为 null,示意整棵 workInProgress fiber 树已处理完毕。do {
// 记录父节点和以后节点的 current 树
const current = completedWork.alternate;
const returnFiber = completedWork.return;
// 查看工作是否实现或是否有货色抛出.
if ((completedWork.effectTag & Incomplete) === NoEffect) {
let next;
if (
!enableProfilerTimer ||
(completedWork.mode & ProfileMode) === NoMode
) {
// 执行 completeWork,并把返回值赋值给 next
next = completeWork(current, completedWork, subtreeRenderLanes);
} else {startProfilerTimer(completedWork);
next = completeWork(current, completedWork, subtreeRenderLanes);
// Update render duration assuming we didn't error.
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
}
resetCurrentDebugFiberInDEV();
if (next !== null) {
// Completing this fiber spawned new work. Work on that next.
workInProgress = next;
return;
}
resetChildLanes(completedWork);
if (
returnFiber !== null &&
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// 执行 effect 相干
if (returnFiber.firstEffect === null) {returnFiber.firstEffect = completedWork.firstEffect;}
if (completedWork.lastEffect !== null) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = completedWork.firstEffect;}
returnFiber.lastEffect = completedWork.lastEffect;
}
if (effectTag > PerformedWork) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = completedWork;} else {returnFiber.firstEffect = completedWork;}
returnFiber.lastEffect = completedWork;
}
}
} else {const next = unwindWork(completedWork, subtreeRenderLanes);
// Because this fiber did not complete, don't reset its expiration time.
if (next !== null) {
next.effectTag &= HostEffectMask;
workInProgress = next;
return;
}
if (
enableProfilerTimer &&
(completedWork.mode & ProfileMode) !== NoMode
) {stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
let actualDuration = completedWork.actualDuration;
let child = completedWork.child;
while (child !== null) {
actualDuration += child.actualDuration;
child = child.sibling;
}
completedWork.actualDuration = actualDuration;
}
if (returnFiber !== null) {
// Mark the parent fiber as incomplete and clear its effect list.
returnFiber.firstEffect = returnFiber.lastEffect = null;
returnFiber.effectTag |= Incomplete;
}
}
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
workInProgress = siblingFiber;
return;
}
// 赋值父节点
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
// We've reached the root.
if (workInProgressRootExitStatus === RootIncomplete) {workInProgressRootExitStatus = RootCompleted;}
}
运行流程
completeWork
如果说“递”阶段的 beginWork 办法次要是创立子节点,那么“归”阶段的 completeWork 办法则次要是创立以后节点的 DOM 节点,并对子节点的 DOM 节点和 EffectList 进行收拢。很多类型是不进行解决,return null
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
case ClassComponent: {
// ... 省略
return null;
}
case HostRoot: {
// ... 省略
return null;
}
case HostComponent: {
// ...
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
// 更新 dom 节点
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (enableDeprecatedFlareAPI) {
const prevListeners = current.memoizedProps.DEPRECATED_flareListeners;
const nextListeners = newProps.DEPRECATED_flareListeners;
if (prevListeners !== nextListeners) {markUpdate(workInProgress);
}
}
if (current.ref !== workInProgress.ref) {markRef(workInProgress);
}
} else {
//...
const currentHostContext = getHostContext();
const wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {// 服务端渲染相干} else {
// 创立新的 dom 节点
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 把 fiber 子节点的 dom 挂载到以后 dom 前面
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance;
if (enableDeprecatedFlareAPI) {
const listeners = newProps.DEPRECATED_flareListeners;
if (listeners != null) {
updateDeprecatedEventListeners(
listeners,
workInProgress,
rootContainerInstance,
);
}
}
if (
// 初始化 dom 属性和事件
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {markUpdate(workInProgress);
}
}
if (workInProgress.ref !== null) {markRef(workInProgress);
}
}
return null;
}
// ... 省略
}
createInstance
新建节点,调用 createElement 创立 dom 节点
function createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): Instance {
let parentNamespace: string;
if (__DEV__) {
// TODO: take namespace into account when validating.
const hostContextDev = ((hostContext: any): HostContextDev);
validateDOMNesting(type, null, hostContextDev.ancestorInfo);
if (
typeof props.children === 'string' ||
typeof props.children === 'number'
) {
const string = '' + props.children;
const ownAncestorInfo = updatedAncestorInfo(
hostContextDev.ancestorInfo,
type,
);
validateDOMNesting(null, string, ownAncestorInfo);
}
parentNamespace = hostContextDev.namespace;
} else {parentNamespace = ((hostContext: any): HostContextProd);
}
const domElement: Instance = createElement(
type,
props,
rootContainerInstance,
parentNamespace,
);
precacheFiberNode(internalInstanceHandle, domElement);
// 更新属性
updateFiberProps(domElement, props);
return domElement;
}
appendAllChildren
把 fiber 子节点的 dom 挂载到以后 dom 前面
appendAllChildren = function(
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
let node = workInProgress.child;
while (node !== null) {if (node.tag === HostComponent || node.tag === HostText) {
// stateNode 挂载节点的 dom
appendInitialChild(parent, node.stateNode);
} else if (enableFundamentalAPI && node.tag === FundamentalComponent) {appendInitialChild(parent, node.stateNode.instance);
} else if (node.tag === HostPortal) {} else if (node.child !== null) {
// 针对一些非凡类型的子节点,如 <Fragment />,尝试从子节点的子节点获取 DOM
// 存在子节点就持续遍历子节点
node.child.return = node;
node = node.child;
continue;
}
if (node === workInProgress) {return;}
while (node.sibling === null) {if (node.return === null || node.return === workInProgress) {return;}
node = node.return;
}
node.sibling.return = node.return;
// 将 node.sibling 作为下次循环的主体
node = node.sibling;
}
};
// 执行了原生的 appendChild 办法
export function appendInitialChild(parentInstance: Instance, child: Instance | TextInstance): void {parentInstance.appendChild(child);
}
updateHostComponent
更新旧的 dom 节点,次要作用就是计算出须要变动的 DOM 节点属性,并给以后节点打上 Update 的 EffectTag。
updateHostComponent = function(
current: Fiber,
workInProgress: Fiber,
type: Type,
newProps: Props,
rootContainerInstance: Container,
) {
// If we have an alternate, that means this is an update and we need to
// schedule a side-effect to do the updates.
const oldProps = current.memoizedProps;
// props 没有变动就间接返回
if (oldProps === newProps) {return;}
const updatePayload = prepareUpdate(
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
// 将计算出来的 updatePayload 挂载在 workInProgress.updateQueue 上,供后续 commit 阶段应用
workInProgress.updateQueue = (updatePayload: any);
// 如果 updatePayload 不为空,则给以后节点打上 Update 的 EffectTag
if (updatePayload) {markUpdate(workInProgress);
}
};
总结
- completeUnitOfWork 办法次要循环执行 completeWork,父元素为空或者存在兄弟节点就会进行下一轮 render 阶段解析,生成兄弟节点的 fiber。
- completeWork 次要是生成以后 fiber 的 dom 节点,并且挂载连接子节点的 dom
- completeWork 次要应用 createInstance 新建节点和 updateHostComponent 更新节点操作。
- 最终完结 completeUnitOfWork 执行,进入 commit 阶段(下个文章开始讲,敬请期待)