这次是接着上一期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阶段(下个文章开始讲,敬请期待)