這次是接著上一期render階段的文章秧饮,解析掛載時(shí)render階段的"歸"階段----completeWork函數(shù)
completeWork開始階段
在performUnitOfWork中執(zhí)行完beginWork,就會(huì)進(jìn)入下面判斷
workInProgress.child
if (next === null) {
// workInProgress已經(jīng)不存在子樹峰尝,就開始進(jìn)行"歸"階段
completeUnitOfWork(unitOfWork);
} else {
// next是beginWork調(diào)用后的返回值workInProgress.child
workInProgress = next;
}
completeUnitOfWork
主要做了兩件事甘畅,執(zhí)行completeWork 和收攏EffectList
function completeUnitOfWork(unitOfWork: Fiber): void {
//嘗試完成當(dāng)前的工作單元慨蓝,然后移動(dòng)到下一個(gè)兄弟剧防。 如果沒有更多的兄弟京革,返回到父fiber奇唤。
let completedWork = unitOfWork;
// 直到父節(jié)點(diǎn)為null,表示整棵 workInProgress fiber 樹已處理完畢匹摇。
do {
// 記錄父節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)的current樹
const current = completedWork.alternate;
const returnFiber = completedWork.return;
// 檢查工作是否完成或是否有東西拋出.
if ((completedWork.effectTag & Incomplete) === NoEffect) {
let next;
if (
!enableProfilerTimer ||
(completedWork.mode & ProfileMode) === NoMode
) {
// 執(zhí)行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
) {
// 執(zhí)行effect相關(guān)
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;
}
// 賦值父節(jié)點(diǎn)
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
// We've reached the root.
if (workInProgressRootExitStatus === RootIncomplete) {
workInProgressRootExitStatus = RootCompleted;
}
}
運(yùn)行流程
運(yùn)行流程
completeWork
如果說“遞”階段的 beginWork 方法主要是創(chuàng)建子節(jié)點(diǎn),那么“歸”階段的 completeWork 方法則主要是創(chuàng)建當(dāng)前節(jié)點(diǎn)的 DOM 節(jié)點(diǎn)来惧,并對(duì)子節(jié)點(diǎn)的 DOM 節(jié)點(diǎn)和 EffectList 進(jìn)行收攏冗栗。很多類型是不進(jìn)行處理,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節(jié)點(diǎn)
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) {
// 服務(wù)端渲染相關(guān)
} else {
// 創(chuàng)建新的dom節(jié)點(diǎn)
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 把fiber子節(jié)點(diǎn)的dom掛載到當(dāng)前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
新建節(jié)點(diǎn)供搀,調(diào)用createElement創(chuàng)建dom節(jié)點(diǎn)
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子節(jié)點(diǎn)的dom掛載到當(dāng)前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掛載節(jié)點(diǎn)的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) {
// 針對(duì)一些特殊類型的子節(jié)點(diǎn)隅居,如<Fragment />,嘗試從子節(jié)點(diǎn)的子節(jié)點(diǎn)獲取DOM
// 存在子節(jié)點(diǎn)就繼續(xù)遍歷子節(jié)點(diǎn)
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作為下次循環(huán)的主體
node = node.sibling;
}
};
// 執(zhí)行了原生的appendChild方法
export function appendInitialChild(parentInstance: Instance, child: Instance | TextInstance): void {
parentInstance.appendChild(child);
}
updateHostComponent
更新舊的dom節(jié)點(diǎn)葛虐,主要作用就是計(jì)算出需要變化的 DOM 節(jié)點(diǎn)屬性胎源,并給當(dāng)前節(jié)點(diǎn)打上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,
);
// 將計(jì)算出來的updatePayload掛載在workInProgress.updateQueue上屿脐,供后續(xù)commit階段使用
workInProgress.updateQueue = (updatePayload: any);
// 如果updatePayload不為空涕蚤,則給當(dāng)前節(jié)點(diǎn)打上Update的EffectTag
if (updatePayload) {
markUpdate(workInProgress);
}
};
總結(jié)
- completeUnitOfWork方法主要循環(huán)執(zhí)行completeWork宪卿,父元素為空或者存在兄弟節(jié)點(diǎn)就會(huì)進(jìn)行下一輪render階段解析,生成兄弟節(jié)點(diǎn)的fiber万栅。
- completeWork主要是生成當(dāng)前fiber的dom節(jié)點(diǎn)佑钾,并且掛載連接子節(jié)點(diǎn)的dom
- completeWork主要使用createInstance新建節(jié)點(diǎn)和updateHostComponent更新節(jié)點(diǎn)操作。
- 最終結(jié)束completeUnitOfWork執(zhí)行烦粒,進(jìn)入commit階段(下個(gè)文章開始講休溶,敬請(qǐng)期待)
流程