react源码debugger-commit阶段的完成
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react源码debugger-commit阶段的完成相关的知识,希望对你有一定的参考价值。
commit阶段
上节讲到了rootFiber完成completeWork的时候,返回了一个状态,为RootInCompleted,表示工作完成。调用finishConcurrentRender
方法,该方法会调用commitRoot,开启commit阶段。
现在回顾一下,我们的fiber结构是
// App组件
const App: React.FC = () =>
return <DD />;
;
// DD组件
class DD extends Component
render()
return <div>123</div>;
那么fiber结构应该是
rootFiber.child => App fiber
App fiber.child => DD fiber
DD fiber.child => div fiber
看看finishConcurrentRender方法
//完成了render阶段之后,开启commit阶段
finishConcurrentRender(root, exitStatus, lanes);
root是FiberRoot,exitStatus是RootInCompleted,表示完成状态。
finishConcurrentRender
function finishConcurrentRender(root, exitStatus, lanes)
switch(exitStatus)
case RootInProgress:
case RootFatalErrored: throw new Error('Root did not complete. This is a bug in React.')
case RootCompleted: commitRoot(root, workInProgressRootRecoverableErrors); break;
...
可以看到,finishConcurrentRender主要就是完成了对exitStatus的判断,如果状态不对就抛出错误。然后调用commitRoot方法,开启commit阶段。
commitRoot
commitRoot会调用commitRootImpl方法,该方法时commit阶段的主要方法。
commitRootImpl方法
commitRootImp主要做了一下六件事情。
- 1 开始执行dom操作之前,将所有effects执行完毕。
- 2 before-mutation之前的阶段,全局变量重置,调度useEffect
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<mixed>,
renderPriorityLevel: EventPriority
)
// --------before-mutation-之前的阶段-start-------
const finishedWork = root.finishedWork; // rootFiber
const lanes = root.finishedLanes; //优先级
// 重置FiberRoot的属性
root.finishedWork = null;
root.finishedLanes = NoLanes;
// 重置变量
root.callbackNode = null;
root.callbackPriority = NoLane;
//开始调度useEffect
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
)
if (!rootDoesHavePassiveEffects)
// 赋值全局变量,表示有useEffect的副作用
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
// 以普通优先级调度useEffect
scheduleCallback(NormalSchedulerPriority, () =>
flushPassiveEffects();
// This render triggered passive effects: release the root cache pool
// *after* passive effects fire to avoid freeing a cache pool that may
// be referenced by a node in the tree (HostRoot, Cache boundary etc)
return null;
);
......
- 3 before-mutation阶段,调用commitBeforeMutationEffects
- 4 mutation阶段,调用commitMutationEffects
- 5 layout阶段, 调用commitLayoutEffects
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<mixed>,
renderPriorityLevel: EventPriority
)
// --------before-mutation-之前的阶段-start-------
......
// 判断是否有effects影响需要更新
const subtreeHasEffects = ....// 子树是否有更新
const rootHasEffect = .... // root是否有更新
if (subtreeHasEffects || rootHasEffect)
//因为commit是同步的,优先级也是最高的
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(DiscreteEventPriority); //设置最高优先级
// -----------------beforeMutation阶段------------------
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
root,
finishedWork
);
// ------------mutation阶段------------------
commitMutationEffects(root, finishedWork, lanes);
// 切换RootFiber.current
root.current = finishedWork;
// ------------layout阶段----------------
commitLayoutEffects(finishedWork, root, lanes);
if (__DEV__)
if (enableDebugTracing)
logLayoutEffectsStopped();
- 6 layout之后阶段,如果有useEffect的effects,就赋值给全局变量rootWithPendingPassiveEffects,useEffect的调度函数通过上面去获取effectLists,执行对应的useEffects函数。调用ensureRootIsScheduled最后判断还有没有更新没执行
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<mixed>,
renderPriorityLevel: EventPriority
)
// --------before-mutation-之前的阶段-start-------
......
// 判断是否有effects影响需要更新
const subtreeHasEffects = ....// 子树是否有更新
const rootHasEffect = .... // root是否有更新
if (subtreeHasEffects || rootHasEffect)
//因为commit是同步的,优先级也是最高的
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(DiscreteEventPriority); //设置最高优先级
// -----------------beforeMutation阶段------------------
// ------------mutation阶段------------------
// ------------layout阶段----------------
....
// -------------------layout之后--start-------------------
const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
if (rootDoesHavePassiveEffects) // 有useEffect的effects
// This commit has passive effects. Stash a reference to them. But don't
// schedule a callback until after flushing layout work.
rootDoesHavePassiveEffects = false;
rootWithPendingPassiveEffects = root; //将root赋值给rootWithPendingPassiveEffects,useEffect执行的时候会通过他来获取effectList
pendingPassiveEffectsLanes = lanes;
...
ensureRootIsScheduled(root, now()); //确保额外的工作正在调度
// If layout work was scheduled, flush it now.
flushSyncCallbacks();
...
commit阶段完成。
接着看对应的每个阶段做的事情。
before-mutation之前
//当rootWithPendingPassiveEffects不为空的时候,表示有effect 执行effectList上的副作用,直到effectLists上值为null
do
flushPassiveEffects();
while (rootWithPendingPassiveEffects !== null); //mount的时候为null,
const finishedWork = root.finishedWork; // rootFiber
const lanes = root.finishedLanes; //优先级
// 重置FiberRoot的属性
root.finishedWork = null;
root.finishedLanes = NoLanes;
// 重置变量
root.callbackNode = null;
root.callbackPriority = NoLane;
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
)
if (!rootDoesHavePassiveEffects)
// 赋值全局变量,表示有useEffect的副作用
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
scheduleCallback(NormalSchedulerPriority, () => // 以普通优先级调度useEffect
flushPassiveEffects();
// This render triggered passive effects: release the root cache pool
// *after* passive effects fire to avoid freeing a cache pool that may
// be referenced by a node in the tree (HostRoot, Cache boundary etc)
return null;
);
可以看到,主要就是三件事情
- 如果rootWithPendingPassiveEffects有值,就调度flushPassiveEffects,调度useEffect
- 重置全局变量
- 如果有useEffect的相关effects,就调用scheduleCallback,以普通优先级调度flushPassiveEffects
flushPassiveEffects最终会调用
export function flushPassiveEffects(): boolean
if (rootWithPendingPassiveEffects !== null)
// 调用UseEffect的销毁函数
commitPassiveUnmountEffects(root.current);
// 调用useEffect函数
commitPassiveMountEffects(root, root.current);
调度useEffect的销毁和执行函数。
rootWithPendingPassiveEffects是在layout阶段之后被赋值的。
before-mutation阶段
- 根据effectList链表获取有副作用的fiber
- 类组件执行instance.getSnapShotBeforeUpdate,将返回值挂载到instance上面。
export function commitBeforeMutationEffects(
root: FiberRoot,
firstChild: Fiber // workInprogress rootFiber
)
...
nextEffect = firstChild;
commitBeforeMutationEffects_begin();
..
return shouldFire;
firstCHild就是rootFiber,对于commit阶段,他就是第一个要处理的子节点。
调用commitBeforeMutationEffects_begin
function commitBeforeMutationEffects_begin()
// 开启while循环
while (nextEffect !== null)
const fiber = nextEffect;
const child = fiber.child;
if (
// mount的时候只有rootFiber身材有flags,所以第一次mount,nextEffect最后会被赋值到App
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
child !== null
)
ensureCorrectReturnPointer(child, fiber);
nextEffect = child;
else
commitBeforeMutationEffects_complete();
开启while循环,遍历effectLists链表,获取到最后一个有副作用的子节点。
mount的时候需要注意,因为只有rootFiber会有flags标记,所以第一次循环,会走if条件,将App fiber赋值给nextEffect。
第二次循环因为App fiber没有flags标记,所以走else条件,commitBeforeMutationEffects_complete
function commitBeforeMutationEffects_complete()
while (nextEffect !== null)
// 开启while循环
const fiber = nextEffect;
commitBeforeMutationEffectsOnFiber(fiber);
const sibling = fiber.sibling;
if (sibling !== null)
ensureCorrectReturnPointer(sibling, fiber.return);
nextEffect = sibling;
return;
// 下一个while循环条件
nextEffect = fiber.return;
commitBeforeMutationEffects_complete也开启一个while循环,从下往上处理每一个有flags的fiber。调用commitBeforeMutationEffectsOnFiber
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber)
const current = finishedWork.alternate; //获取rooFiber的alternate也就是current rootFiber
const flags = finishedWork.flags; //获取rootFiber的effectTag
if ((flags & Snapshot) !== NoFlags)
//有flags标记的fiber才会执行
switch (finishedWork.tag)
..
case ClassComponent:
if (current !== null)
const prevProps = current.memoizedProps; //旧的props
const prevState = current.memoizedState; //旧的state
const instance = finishedWork.stateNode; //获取类组件的实例
// 调用类组件的getSnapshotBeforeUpdate函数
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState
);
// 将返回的快照值保存在实例的__reactInternalSnapshotBeforeUpdate上,到时候赋值给componentDidUpdate
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
break;
这里主要处理了类组件,执行了getSnapshotBeforeUpdate生命周期,将返回值挂载到
instance.__reactInternalSnapshotBeforeUpdate上。
mutation阶段
主要调用了commitMutationEffects函数,mutation阶段是操作dom的阶段。
commitMutationEffects(root, finishedWork, lanes);
function commitMutationEffects_begin(root: FiberRoot, lanes: Lanes)
while (nextEffect !== null)
const fiber = nextEffect;
// 删除操作
const deletions = fiber.deletions;
if (deletions !== null)
for (let i = 0; i < deletions.length; i++)
commitDeletion(root, childToDelete, fiber); //对于删除的fiber
const child = fiber.child;
// 跟before-mutation阶段一样的判断条件
if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null)
ensureCorrectReturnPointer(child, fiber);
nextEffect = child;
else
commitMutationEffects_complete(root, lanes);
commitMutationEffects_begin函数的while循环,跟before-mutation阶段的一样判断条件。这里多了一个对于删除节点的操作。
对于删除的节点,存在fiber.deletion上。调用commitDeletion进行删除。
commitDeletion调用commitNestedUnmounts函数。
主要执行对dom的删除。
function commitNestedUnmounts(
finishedRoot: FiberRoot,
root: Fiber,
nearestMountedAncestor: Fiber
)
// 递归删除所有子节点,将子节点都调用一次commitUnmount函数。
while (true)
commitUnmount(finishedRoot, node, nearestMountedAncestor);
if (
node.child !== null &&
(!supportsMutation || node.tag !== HostPortal)
)
node.child.return = node;
node = node.child;
continue;
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
)
switch (current.tag)
case FunctionComponent:
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
// 调用useLyaoutEffect的销毁函数
const destroy, tag = effect;
if (destroy !== undefined)
....
safelyCallDestroy(current, nearestMountedAncestor, destroy);
...
effect = effect.next;
while (effect !== firstEffect);
return;
.....
// 对于类组件
case ClassComponent:
safelyDetachRef(current, nearestMountedAncestor);
const instance = current.stateNode;
if (typeof instance.componentWillUnmount === "function")
safelyCallComponentWillUnmount(
current,
nearestMountedAncestor,
instance
);
return;
....
....
- useLayoutEffect是以链表的形式存放在fiber.updateQueue.lastEffect之上。这里对于函数组件的操作就是获取所有effects,调用其销毁函数destroy。记住,useLayoutEffect销毁函数是在mutation阶段执行的。
- 对于类组件,处理ref,调用类组件的componentWillUnMount函数。
commitMutationEffects_complete
跟before-mutation阶段一样
function commitMutationEffects_complete(root: FiberRoot, lanes: Lanes)
while (nextEffect !== null)
// 开启while循环
const fiber = nextEffect;
...
commitMutationEffectsOnFiber(fiber, root, lanes);
...
const sibling = fiber.sibling;
if (sibling !== null)
ensureCorrectReturnPointer(sibling, fiber.return);
nextEffect = sibling;
return;
nextEffect = fiber.return;
开启while循环,执行所有的有flags的fiber,调用commitMutationEffectsOnFiber函数
// mutation阶段主要执行的函数
function commitMutationEffectsOnFiber(
finishedWork: Fiber, // fiber
root: FiberRoot, //FiberROot
lanes: Lanes
)
const flags = finishedWork.flags; //effectTag
// 有ref的effectTag
if (flags & Ref)
const current = finishedWork.alternate;
if (current !== null)
commitDetachRef(current); //清除ref
// 判断当前的fiber要做啥操作
const primaryFlags = flags & (Placement | Update | Hydrating);
switch (primaryFlags)
case Placement:
// 新增
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
break;
case PlacementAndUpdate:
// 新增并且修改
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
// Update
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
...
case Update:
// 修改
const current = finishedWork.alternate;
commitWork(current, finishedWork);
break;
commitMutationEffectsOnFiber主要处理了包含有ref的flags的fiber,先清除老的ref,等到layout阶段再赋值新的ref
function commitDetachRef(current: Fiber)
const currentRef = current.ref;
if (currentRef !== null)
if (typeof currentRef === "function")
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
current<以上是关于react源码debugger-commit阶段的完成的主要内容,如果未能解决你的问题,请参考以下文章