React Commit阶段
目录
前言
本文是React源码学习系列第四篇,该系列整体都基于React18.0.0版本源码。旨在学习过程中记录一些个人的理解。该篇介绍React Commit过程。
入口
承接上文,在完成了Render阶段以后已经拥有了一棵完整的workInProgress Fiber Tree。
// 保留核心代码
function performConcurrentWorkOnRoot(root, didTimeout) {
// 忽略
if (exitStatus !== RootInProgress) {
if (exitStatus === RootDidNotComplete) {
// 忽略未完成
} else {
// 到这里说明已经完成了Render阶段
const finishedWork: Fiber = (root.current.alternate:any);
// 到这里已经拥有了一棵完整的 workInProgress Fiber tree。
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
// 进入提交阶段
finishConcurrentRender(root, exitStatus, lanes);
}
}
return null;
}
finishConcurrentRender
// 保留核心代码
function finishConcurrentRender(root, exitStatus, lanes) {
// 忽略其他状态
commitRoot(root, workInProgressRootRecoverableErrors);
}
commitRoot
进入commit阶段后不可打断。commit阶段包含三个子阶段,及flushPassiveEffects。
- BeforeMutation阶段
- Mutation阶段
- Layout阶段
// 保留核心代码
function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
// 缓存当前优先级及transition环境。
const previousUpdateLanePriority = getCurrentUpdatePriority();
const prevTransition = ReactCurrentBatchConfig.transition;
try {
ReactCurrentBatchConfig.transition = null;
// 手动设置为同步优先级
setCurrentUpdatePriority(DiscreteEventPriority);
commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);
} finally {
// 恢复优先级及transition环境。
ReactCurrentBatchConfig.transition = prevTransition;
setCurrentUpdatePriority(previousUpdateLanePriority);
}
return null;
}
commitRootImpl 这里会执行commit阶段的三个小阶段。并以异步方式执行useEffect,这就是为什么useEffect会在useLayoutEffect后面执行的原因。
// 保留核心代码
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<mixed>,
renderPriorityLevel: EventPriority,
) {
// flushPassiveEffects方法内会flushSyncCallbacks方法,遍历执行所有同步优先级调度的更新,执行过程中有可能会产生新的useEffect,所以需要放在do while循环中,确保所有useEffect回调都执行完毕。
do {
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
const finishedWork = root.finishedWork;
const lanes = root.finishedLanes;
if (finishedWork === null) {
return null;
}
root.finishedWork = null;
root.finishedLanes = NoLanes;
// 重置
root.callbackNode = null;
root.callbackPriority = NoLane;
// 剩余未处理的lanes
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
// FiberRootNode上去掉已完成的lanes
markRootFinished(root, remainingLanes);
if (root === workInProgressRoot) {
// 重置
workInProgressRoot = null;
workInProgress = null;
workInProgressRootRenderLanes = NoLanes;
}
// 检查整棵Fiber树上是否存在useEffect,有的话安排一个调度异步执行。
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
// 已默认优先级调度执行useEffect。
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
// 检查整棵Fiber树上是否存在useEffect、layoutEffect、useInsertionEffect
const subtreeHasEffects =
(finishedWork.subtreeFlags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
// 检查HostRootFiber节点上是否存在useEffect、layoutEffect、useInsertionEffect
const rootHasEffect =
(finishedWork.flags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
// 子节点有Effect或者本身有Effect
if (subtreeHasEffects || rootHasEffect) {
// 缓存环境
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = null;
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(DiscreteEventPriority);
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
ReactCurrentOwner.current = null;
// BeforeMutation阶段
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
root,
finishedWork,
);
// Mutation阶段
commitMutationEffects(root, finishedWork, lanes);
// 切换fiber树
// 正在进行的树现在是当前树。这必须在mutation阶段之后进行,以便在componentWillUnmount期间前一个树仍然是current,
// 但在layout阶段之前,以便在componentDidMount/Update期间完成的工作是finishedWork的。
root.current = finishedWork;
// layout阶段
commitLayoutEffects(finishedWork, root, lanes);
// 恢复之前环境
executionContext = prevExecutionContext;
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
} else {
// 没有任何Effect 切换fiber树
root.current = finishedWork;
}
const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
// 此次提交有useEffect。缓存他们。
if (rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = false;
rootWithPendingPassiveEffects = root;
pendingPassiveEffectsLanes = lanes;
}
// 再读一遍,因为一个Effect可能已经更新了它
remainingLanes = root.pendingLanes;
// 退出之前,安排一个新的调度。
ensureRootIsScheduled(root, now());
// 如果本次更新有SyncLane优先级,同步执行useEffect。
if (
includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
root.tag !== LegacyRoot
) {
flushPassiveEffects();
}
// 提交阶段如果有新的同步更新产生,执行它
flushSyncCallbacks();
return null;
}
useEffect
flushPassiveEffects
// 保留核心代码
function flushPassiveEffects(): boolean {
if (rootWithPendingPassiveEffects !== null) {
return flushPassiveEffectsImpl();
}
return false;
}
flushPassiveEffectsImpl
// 保留核心代码
function flushPassiveEffectsImpl() {
if (rootWithPendingPassiveEffects === null) {
return false;
}
const root = rootWithPendingPassiveEffects;
const lanes = pendingPassiveEffectsLanes;
rootWithPendingPassiveEffects = null;
pendingPassiveEffectsLanes = NoLanes;
commitPassiveUnmountEffects(root.current);
commitPassiveMountEffects(root, root.current, lanes);
flushSyncCallbacks();
return true;
}
卸载阶段
commitPassiveUnmountEffects,共有两个递归。
递阶段
自上而下,直到叶子节点或者子孙节点没有PassiveMask标记。过程中如果有Fiber带ChildDeletion标记。进入子递阶段。
// 保留核心代码
function commitPassiveUnmountEffects(firstChild: Fiber): void {
nextEffect = firstChild;
commitPassiveUnmountEffects_begin();
}
function commitPassiveUnmountEffects_begin() {
while (nextEffect !== null) {
const fiber = nextEffect;
const child = fiber.child;
// 有删除子节点标记
if ((nextEffect.flags & ChildDeletion) !== NoFlags) {
const deletions = fiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const fiberToDelete = deletions[i];
nextEffect = fiberToDelete;
// 为打上删除标记的节点 执行卸载副作用
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
fiberToDelete,
fiber,
);
}
// 子节点删除完后,nextEffect恢复到该有删除子节点flags的fiber 继续下一步
nextEffect = fiber;
}
}
// 一直向下找,直到叶子节点或者子孙节点没有PassiveMask标记
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
nextEffect = child;
} else {
// 该节点子孙节点没有PassiveMask标记或者已经是叶子节点,进入归操作
commitPassiveUnmountEffects_complete();
}
}
}
子递阶段
遍历该Fiber.deletions上的待删除Fiber,为每一个Fiber开启一个新的递归。 自上而下直到到达叶子节点,执行删除Fiber及其子孙Fiber的useEffect卸载函数。到达叶子节点后进入子归阶段。
// 保留核心代码
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
deletedSubtreeRoot: Fiber,
nearestMountedAncestor: Fiber | null,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
// 给删除的节点执行useEffect卸载函数。
commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
const child = fiber.child;
if (child !== null) {
nextEffect = child;
} else {
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
deletedSubtreeRoot,
);
}
}
}
// 给删除的节点执行useEffect卸载函数。
function commitPassiveUnmountInsideDeletedTreeOnFiber(
current: Fiber,
nearestMountedAncestor: Fiber | null,
): void {
switch (current.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListUnmount(
HookPassive,
current,
nearestMountedAncestor,
);
break;
}
// 忽略其他类型
}
}
子归阶段
自下而上直到到达有删除子节点标记的Fiber,并清空沿途的Fiber的属性,跳出循环,回到第一个循环。
// 保留核心代码
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
deletedSubtreeRoot: Fiber,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
const sibling = fiber.sibling;
const returnFiber = fiber.return;
// 清空闪促Fiber的属性
detachFiberAfterEffects(fiber);
// 归到待删除的根节点以后退出
if (fiber === deletedSubtreeRoot) {
nextEffect = null;
return;
}
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = returnFiber;
}
}
归阶段
给所有有PassiveMask标记的Fiber执行useEffect卸载函数。
function commitPassiveUnmountEffects_complete() {
while (nextEffect !== null) {
const fiber = nextEffect;
// 该节点有useEffect
if ((fiber.flags & Passive) !== NoFlags) {
commitPassiveUnmountOnFiber(fiber);
}
const sibling = fiber.sibling;
// 有兄弟节点,继续为兄弟节点执行begin操作
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
// 执行useEffect的卸载函数
commitHookEffectListUnmount(
HookPassive | HookHasEffect,
finishedWork,
finishedWork.return,
);
break;
}
}
}
commitHookEffectListUnmount 该方法为Effect公用方法,通过传递进来的HookFlags过滤出本次需要执行的Effect类型。
// 保留核心代码
function commitHookEffectListUnmount(
flags: HookFlags,
finishedWork: Fiber,
nearestMountedAncestor: Fiber | null,
) {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
// updateQueue里面保存着该Fiber里面所有的Effect。
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
// 通过传进来的flags 过滤出需要执行的effect
if ((effect.tag & flags) === flags) {
const destroy = effect.destroy;
effect.destroy = undefined;
if (destroy !== undefined) {
// 执行卸载函数
safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
// 执行卸载函数
function safelyCallDestroy(
current: Fiber,
nearestMountedAncestor: Fiber | null,
destroy: () => void,
) {
try {
destroy();
} catch (error) {
// ...
}
}
加载阶段
自上而下,直到到达叶子节点或者子孙节点都不包含PassiveMask标记,进入归阶段,执行useEffect create函数。
// 保留核心代码
function commitPassiveMountEffects(
root: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
nextEffect = finishedWork;
commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
}
function commitPassiveMountEffects_begin(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
const firstChild = fiber.child;
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
nextEffect = firstChild;
} else {
commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
}
}
}
function commitPassiveMountEffects_complete(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
if ((fiber.flags & Passive) !== NoFlags) {
commitPassiveMountOnFiber(root, fiber, committedLanes);
}
if (fiber === subtreeRoot) {
nextEffect = null;
return;
}
const sibling = fiber.sibling;
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
function commitPassiveMountOnFiber(
finishedRoot: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
break;
}
// ...
}
}
commitHookEffectListMount 该方法为Effect公用方法,通过传递进来的HookFlags过滤出本次需要执行的Effect类型。
// 保留核心代码
function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
const updateQueue = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
const create = effect.create;
// 执行effect
effect.destroy = create();
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
BeforeMutation阶段
递阶段
自上而下,直到到达叶子节点或者子孙节点都不包含BeforeMutationMask标记,进入归阶段。
// 保留核心代码
function commitBeforeMutationEffects(
root: FiberRoot,
firstChild: Fiber,
) {
nextEffect = firstChild;
commitBeforeMutationEffects_begin();
}
function commitBeforeMutationEffects_begin() {
while (nextEffect !== null) {
const fiber = nextEffect;
const child = fiber.child;
if (
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
child !== null
) {
nextEffect = child;
} else {
// 子节点不包含BeforeMutationMask或者叶子节点
commitBeforeMutationEffects_complete();
}
}
}
归阶段
- ClassComponent执行getSnapshotBeforeUpdate生命周期方法。
- HostRootFiber,清除#root内容。
// 保留核心代码
function commitBeforeMutationEffects_complete() {
while (nextEffect !== null) {
const fiber = nextEffect;
commitBeforeMutationEffectsOnFiber(fiber);
const sibling = fiber.sibling;
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
const current = finishedWork.alternate;
const flags = finishedWork.flags;
// 只处理带Update、Snapshot的flags的fiber
if ((flags & Snapshot) !== NoFlags) {
switch (finishedWork.tag) {
case ClassComponent: {
// 只有ClassComponent有Snapshot标记
if (current !== null) {
// 调用getSnapshotBeforeUpdate
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
}
break;
}
case HostRoot: {
if (supportsMutation) {
const root = finishedWork.stateNode;
// HostRootFiber 清除#root内容
clearContainer(root.containerInfo);
}
break;
}
// ...
}
}
}
Mutation阶段
该阶段操作真实的DOM。
- 递阶段,如果Fiber上有deletions(待删除的Fiber)。执行卸载回调函数,如果是DOM或者文本类型Fiber,删除对应的DOM。
- 归阶段,根据不同的flags,进行不同的处理。
递阶段
// 保留核心代码
function commitMutationEffects(
root: FiberRoot,
firstChild: Fiber,
committedLanes: Lanes,
) {
commitMutationEffects_begin(root, committedLanes);
}
function commitMutationEffects_begin(root: FiberRoot, lanes: Lanes) {
while (nextEffect !== null) {
const fiber = nextEffect;
// Fiber.deletions,在beginwork Diff阶段打上
const deletions = fiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
commitDeletion(root, childToDelete, fiber);
}
}
const child = fiber.child;
if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
nextEffect = child;
} else {
// subtreeFlags没有MutationMask或者叶子节点
commitMutationEffects_complete(root, lanes);
}
}
}
commitDeletion
给删除掉的Fiber节点执行下列操作。
- FC组件调用带有HookInsertion、HookLayout标记的Effect卸载函数。
- ClassComponent组件调用componentWillUnmount生命周期函数。
- HostComponent组件清空Ref。
- 删掉真实的DOM。
- 断开删除Fiber的return指针。
// 保留核心代码
function commitDeletion(
finishedRoot: FiberRoot,
current: Fiber,
nearestMountedAncestor: Fiber,
): void {
unmountHostComponents(finishedRoot, current, nearestMountedAncestor);
// 断开return指针
detachFiberMutation(current);
}
function unmountHostComponents(
finishedRoot: FiberRoot,
current: Fiber,
nearestMountedAncestor: Fiber,
): void {
// 我们只删除了顶部的Fiber,我们需要递归它的子节点。
let node: Fiber = current;
let currentParentIsValid = false;
// 这两个变量必须总是一起更新。
let currentParent;
let currentParentIsContainer;
while (true) {
if (!currentParentIsValid) {
let parent = node.return;
findParent: while (true) {
// 真实的DOM节点
const parentStateNode = parent.stateNode;
// 找到以下几种tag 退出findParent循环
switch (parent.tag) {
case HostComponent:
currentParent = parentStateNode;
currentParentIsContainer = false;
break findParent;
case HostRoot:
currentParent = parentStateNode.containerInfo;
currentParentIsContainer = true;
break findParent;
case HostPortal:
currentParent = parentStateNode.containerInfo;
currentParentIsContainer = true;
break findParent;
}
parent = parent.return;
}
currentParentIsValid = true;
}
if (node.tag === HostComponent || node.tag === HostText) {
// 递归执行卸载生命周期
commitNestedUnmounts(finishedRoot, node, nearestMountedAncestor);
// 在卸载了所有子节点之后,现在可以安全地从树中删除该节点了。
if (currentParentIsContainer) {
// 删除dom 调用Dom方法container.removeChild(child);
// 该方法做了是否注释判断,如果是注释,取它的父节点
removeChildFromContainer(
((currentParent: any): Container),
(node.stateNode: Instance | TextInstance),
);
} else {
// 删除dom 调用Dom方法parentInstance.removeChild(child);
removeChild(
((currentParent: any): Instance),
(node.stateNode: Instance | TextInstance),
);
}
} else if (
enableSuspenseServerRenderer &&
node.tag === DehydratedFragment
) {
// 忽略服务端渲染
} else if (node.tag === HostPortal) {
// 忽略HostPortal
} else {
// 递归执行卸载生命周期
commitUnmount(finishedRoot, node, nearestMountedAncestor);
// 访问children,因为我们可能会在下面发现更多的html标签。
if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
}
// 归到fiber自身 退出
if (node === current) {
return;
}
while (node.sibling === null) {
// 父节点为空或者父节点为current
if (node.return === null || node.return === current) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function commitNestedUnmounts(
finishedRoot: FiberRoot,
root: Fiber,
nearestMountedAncestor: Fiber,
): void {
let node: Fiber = root;
while (true) {
// 执行卸载方法
commitUnmount(finishedRoot, node, nearestMountedAncestor);
if (
node.child !== null &&
(!supportsMutation || node.tag !== HostPortal)
) {
node.child.return = node;
node = node.child;
continue;
}
// 归到fiber自身 退出
if (node === root) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === root) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
// 执行卸载方法
function commitUnmount(
finishedRoot: FiberRoot,
current: Fiber,
nearestMountedAncestor: Fiber,
): void {
switch (current.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
// 执行带有HookInsertion、HookLayout标记的Effect卸载函数
const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);
if (updateQueue !== null) {
const lastEffect = updateQueue.lastEffect;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const {destroy, tag} = effect;
if (destroy !== undefined) {
if ((tag & HookInsertion) !== NoHookEffect) {
safelyCallDestroy(current, nearestMountedAncestor, destroy);
} else if ((tag & HookLayout) !== NoHookEffect) {
safelyCallDestroy(current, nearestMountedAncestor, destroy);
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
return;
}
case ClassComponent: {
safelyDetachRef(current, nearestMountedAncestor); // 清空ref
const instance = current.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
// 调用componentWillUnmount
safelyCallComponentWillUnmount(
current,
nearestMountedAncestor,
instance,
);
}
return;
}
case HostComponent: {
safelyDetachRef(current, nearestMountedAncestor);
return;
}
// ...
}
}
// 断开return指针
function detachFiberMutation(fiber: Fiber) {
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.return = null;
}
fiber.return = null;
}
归阶段
// 保留核心代码
function commitMutationEffects_complete(root: FiberRoot, lanes: Lanes) {
while (nextEffect !== null) {
const fiber = nextEffect;
commitMutationEffectsOnFiber(fiber, root, lanes);
const sibling = fiber.sibling;
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
// 会根据Fiber的flags进行不同的处理
function commitMutationEffectsOnFiber(
finishedWork: Fiber,
root: FiberRoot,
lanes: Lanes,
) {
const flags = finishedWork.flags;
if (flags & ContentReset) {
// 重置文本 finishedWork.stateNode.textContent = '';
commitResetTextContent(finishedWork);
}
// 有Ref标记
if (flags & Ref) {
const current = finishedWork.alternate;
// 清空ref
if (current !== null) {
commitDetachRef(current);
}
}
const primaryFlags = flags & (Placement | Update | Hydrating);
outer: switch (primaryFlags) {
// 添加
case Placement: {
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
break;
}
// 添加和更新
case PlacementAndUpdate: {
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
}
// 更新
case Update: {
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
}
}
}
commitPlacement
// 保留关键代码
function commitPlacement(finishedWork: Fiber): void {
// 找到tag为HostRoot|HostPortal|HostComponent 的最近父节点,只有这些组件能挂在DOM
const parentFiber = getHostParentFiber(finishedWork);
switch (parentFiber.tag) {
// dom类型节点
case HostComponent: {
const parent: Instance = parentFiber.stateNode;
// 有ContentReset标记
if (parentFiber.flags & ContentReset) {
// 清空parent的文本内容
// parent.stateNode.textContent = ''
resetTextContent(parent);
// 去掉已处理标记
parentFiber.flags &= ~ContentReset;
}
// 找到插入DOM操作的参照物,参照物本身必须是稳定的,即不能有Placement标记。
const before = getHostSibling(finishedWork);
// 插入DOM
insertOrAppendPlacementNode(finishedWork, before, parent);
break;
}
case HostRoot:
case HostPortal: {
const parent: Container = parentFiber.stateNode.containerInfo;
// 找到插入DOM操作的参照物,参照物本身必须是稳定的,即不能有Placement标记。
const before = getHostSibling(finishedWork);
// 插入DOM
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
break;
}
}
}
getHostParentFiber
找到tag为HostRoot|HostPortal|HostComponent 的最近父节点,只有这些组件能挂在DOM。
// 保留关键代码
function getHostParentFiber(fiber: Fiber): Fiber {
let parent = fiber.return;
while (parent !== null) {
if (isHostParent(parent)) {
return parent;
}
parent = parent.return;
}
}
function isHostParent(fiber: Fiber): boolean {
return (
fiber.tag === HostComponent ||
fiber.tag === HostRoot ||
fiber.tag === HostPortal
);
}
getHostSibling
找到插入DOM操作的参照物,参照物本身必须是稳定的,即不能有Placement标记。
// 保留关键代码
function getHostSibling(fiber: Fiber): ?Instance {
let node: Fiber = fiber;
siblings: while (true) {
// 直到没有兄弟节点
while (node.sibling === null) {
// 父节点为null 或 HostRoot || HostPortal || HostComponent 直接返回null
if (node.return === null || isHostParent(node.return)) {
// 没有找到 返回null
return null;
}
// 继续遍历父节点
node = node.return;
}
node.sibling.return = node.return;
// 继续遍历兄弟节点
node = node.sibling;
while (
node.tag !== HostComponent &&
node.tag !== HostText
) { // 不是html节点
// 有Placement标记,说明该节点本身是新添加或移动的,不稳定。跳到siblings循环继续寻找
if (node.flags & Placement) {
continue siblings;
}
// 没有子节点 || node是HostPortal 跳到siblings循环
if (node.child === null || node.tag === HostPortal) {
continue siblings;
} else { // 继续遍历子节点
node.child.return = node;
node = node.child;
}
}
// 找到flags没有Placement的节点,返回DOM节点
if (!(node.flags & Placement)) {
return node.stateNode;
}
}
}
insertOrAppendPlacementNode
初次渲染阶段DOM在这里一次性插入。
// 保留关键代码
function insertOrAppendPlacementNode(
node: Fiber,
before: ?Instance,
parent: Instance,
): void {
const {tag} = node;
// DOM节点
const isHost = tag === HostComponent || tag === HostText;
if (isHost) {
const stateNode = node.stateNode;
if (before) { // 有稳定的兄弟节点,插入兄弟节点前面
insertBefore(parent, stateNode, before);
} else {
// 插入父节点最后位置
appendChild(parent, stateNode);
}
} else if (tag === HostPortal) {
// 忽略
} else { // 非DOM节点
const child = node.child;
if (child !== null) {
// 取它的子节点递归执行
insertOrAppendPlacementNode(child, before, parent);
// React规定组件都只能返回一个根节点,所以这里不会执行
let sibling = child.sibling;
// 存在兄弟节点 遍历所有兄弟节点递归执行插入
while (sibling !== null) {
insertOrAppendPlacementNode(sibling, before, parent);
sibling = sibling.sibling;
}
}
}
}
commitWork方法。
commitHookEffectListMount和commitHookEffectListUnmount上文已经讲过,不再重述。
// 保留关键代码
function commitWork(current: Fiber | null, finishedWork: Fiber): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
// 执行Fiber上的Hooks,useInsertionEffect的destroy函数。
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
finishedWork.return,
);
// 执行Fiber上的Hooks,useInsertionEffect的create函数。
commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
// 执行Fiber上的Hooks,useLayoutEffct的destroy函数。
commitHookEffectListUnmount(
HookLayout | HookHasEffect,
finishedWork,
finishedWork.return,
);
return;
}
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
if (instance != null) {
const oldProps = current !== null ? current.memoizedProps : newProps;
const type = finishedWork.type;
const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
finishedWork.updateQueue = null;
if (updatePayload !== null) {
// 更新html属性
commitUpdate(
instance,
updatePayload,
type,
oldProps,
newProps,
finishedWork,
);
}
}
return;
}
case HostText: {
const textInstance: TextInstance = finishedWork.stateNode;
const newText: string = finishedWork.memoizedProps;
const oldText: string = current !== null ? current.memoizedProps : newText;
// 更新文本内容
commitTextUpdate(textInstance, oldText, newText);
return;
}
// ...
}
}
切换Fiber Tree
root.current = finishedWork;
为什么在这里切换?
- 在mutation阶段之后,以便在componentWillUnmount期间仍然是current Fiber Tree。
- 在layout阶段之前,以便在componentDidMount/Update期间完成的工作是workInProgress Fiber Tree。
Layout阶段
- 递阶段,深度优先向下寻找,直到叶子节点或者该节点的属性subtreeFlags没有LayoutMask标记。进入归阶段。
- 归阶段,为不同类型的Fiber做不同处理。
递阶段
commitLayoutEffects_begin
// 保留关键代码
function commitLayoutEffects_begin(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
const firstChild = fiber.child;
// 子节点有useLayoutEffect && 有子节点
if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) {
nextEffect = firstChild;
} else {
// 子节点没有useLayoutEffect或者叶子节点 归
commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
}
}
}
归阶段
commitLayoutMountEffects_complete
// 保留关键代码
function commitLayoutMountEffects_complete(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
if ((fiber.flags & LayoutMask) !== NoFlags) {
const current = fiber.alternate;
// 执行layoutEffect副作用函数
commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
}
if (fiber === subtreeRoot) {
nextEffect = null;
return;
}
const sibling = fiber.sibling;
if (sibling !== null) {
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
commitLayoutEffectOnFiber方法。
- FC组件执行commitHookEffectListMount。执行带有HookLayout标记的Effect create方法。
- ClassComponent组件调用componentDidMount或componentDidUpdate生命周期方法。
- 有Ref的加上ref。
// 保留关键代码
function commitLayoutEffectOnFiber(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
// 有LayoutMask标记
if ((finishedWork.flags & LayoutMask) !== NoFlags) {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
// 执行useLayoutEffect 的 create函数
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
break;
}
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.flags & Update) {
if (!offscreenSubtreeWasHidden) {
if (current === null) {
// 调用componentDidMount
instance.componentDidMount();
} else {
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(
finishedWork.type,
current.memoizedProps,
);
const prevState = current.memoizedState;
// 调用componentDidUpdate
instance.componentDidUpdate(
prevProps,
prevState, instance.__reactInternalSnapshotBeforeUpdate,
);
}
}
}
const updateQueue: UpdateQueue<*,> | null = (finishedWork.updateQueue: any);
if (updateQueue !== null) {
// 调用callback this.setState方法的第二个参数
commitUpdateQueue(finishedWork, updateQueue, instance);
}
break;
}
case HostRoot: {
const updateQueue: UpdateQueue<*,> | null = (finishedWork.updateQueue: any);
if (updateQueue !== null) {
let instance = null;
if (finishedWork.child !== null) {
switch (finishedWork.child.tag) {
case HostComponent:
instance = getPublicInstance(finishedWork.child.stateNode);
break;
case ClassComponent:
instance = finishedWork.child.stateNode;
break;
}
}
// 调用callback,root.render方法的第三个参数
commitUpdateQueue(finishedWork, updateQueue, instance);
}
break;
}
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
if (current === null && finishedWork.flags & Update) {
const type = finishedWork.type;
const props = finishedWork.memoizedProps;
// 设置input的foucs,img的src
commitMount(instance, type, props, finishedWork);
}
break;
}
}
}
if (!enableSuspenseLayoutEffectSemantics || !offscreenSubtreeWasHidden) {
if (finishedWork.flags & Ref) {
// 加上ref
commitAttachRef(finishedWork);
}
}
}
Ref
commitAttachRef方法。
// 保留关键代码
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
const instance = finishedWork.stateNode;
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
// 浏览器端是返回自己,估计是为了兼容多端
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
if (typeof ref === 'function') {
// 函数方式设置的ref ref={(ref)=> this.ref = ref}
let retVal = ref(instanceToUse);
} else {
// ref = {ref}
ref.current = instanceToUse;
}
}
}
commitDetachRef方法。
// 保留关键代码
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
if (typeof currentRef === 'function') {
currentRef(null);
} else {
currentRef.current = null;
}
}
}
到这里一次完整的提交流程就结束了。
总结
- 为什么useEffect会比useLayoutEffect执行晚?
- 初次加载如何实现只插入一次真实DOM?
- Fiber树在何时切换? 相信看完文章的同学已经知道了上述两个问题的答案。
转载自:https://juejin.cn/post/7397323474270765090