记录学习过程,如有错误欢迎指出
开始之前(必看)
React version 18.2.0
DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知
我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)
因为使用了深度优先的方式讲解,
耦合比较重,不建议跳着看
commitRoot
commitRoot为sync模式下的commit,commit阶段内又分为3个小阶段
- before mutation
- mutation
- layout
function commitRoot(
root: FiberRoot,
recoverableErrors: null | Array<CapturedValue<mixed>>,
transitions: Array<Transition> | null,
) {
// 保存当前更新优先级
const previousUpdateLanePriority = getCurrentUpdatePriority();
const prevTransition = ReactCurrentBatchConfig.transition;
try {
ReactCurrentBatchConfig.transition = null;
// 将当前优先级设为离散,为同步优先级,不可被打断
// 并且DiscreteEventPriority是最高优先级任务
setCurrentUpdatePriority(DiscreteEventPriority);
//进入commitRootImpl
commitRootImpl(
root,
recoverableErrors,
transitions,
previousUpdateLanePriority,
);
} finally {
// 恢复transition
ReactCurrentBatchConfig.transition = prevTransition;
// 恢复优先级
setCurrentUpdatePriority(previousUpdateLanePriority);
}
return null;
}
commitRootImpl(未完)
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<CapturedValue<mixed>>,
transitions: Array<Transition> | null,
renderPriorityLevel: EventPriority,
) {
// 采用 do while 的作用是,在 useEffect 内部
// 可能会触发新的更新,新的更新可能会触发新的副作用 ,因此需要不断的循环,直到 为 null
// 这一步是为了看看还有没有 没有执行的 useEffect, 有的话先执行他们
do {
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
flushRenderPhaseStrictModeWarningsInDEV();
// finishedWork就是alternate
const finishedWork = root.finishedWork;
// 优先级相关
const lanes = root.finishedLanes;
if (finishedWork === null) {
...
if (enableSchedulingProfiler) {
markCommitStopped();
}
return null;
}
root.finishedWork = null;
root.finishedLanes = NoLanes;
if (finishedWork === root.current) {
throw new Error(
// 无法提交与之前相同的树。这个错误可能是由React的一个bug引起的。请提交一个问题。
'Cannot commit the same tree as before. This error is likely caused by ' +
'a bug in React. Please file an issue.',
);
}
// commitRoot绝不会返回一个continuation;它总是同步完成的。
// 所以我们现在可以清除这些,以允许安排一个新的回调
// 清空回调
root.callbackNode = null;
root.callbackPriority = NoLane;
// 检查哪些车道上不再有任何工作安排,并将这些车道标记为已完成
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
// 请确保考虑到在渲染阶段`被并发事件更新`的车道
// 在渲染阶段,不要把它们标记为完成。
// 这一步主要就是校验是否有被并发更新的车道,然后将并发更新的车道合并
const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
//将remainingLanes中的所有车道标记已完成
markRootFinished(root, remainingLanes);
// 处理光标,重置一些 render 阶段使用的变量
if (root === workInProgressRoot) {
// 我们现在可以重新设置这些,因为它们已经完成了。
workInProgressRoot = null;
workInProgress = null;
workInProgressRootRenderLanes = NoLanes;
} else {
}
// 如果flags/subtreeFlags中存在PassiveMask,即Passive|ChildDeletion
// 就是 调度 useEffect
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
// 也就是说如果使用了useEffect或者是节点有删除的情况
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
pendingPassiveTransitions = transitions;
scheduleCallback(NormalSchedulerPriority, () => {
// 执行flushPassiveEffects() 就是触发 useEffect
flushPassiveEffects();
return null;
});
}
}
// 子树是否有更新
const subtreeHasEffects =
(finishedWork.subtreeFlags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
// root是否有effect
const rootHasEffect =
(finishedWork.flags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
// 存在effect的情况
if (subtreeHasEffects || rootHasEffect) {
// 存在副作用,处理 Fiber 上的副作用
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = null;
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(DiscreteEventPriority);
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
// 在调用生命周期之前,将其重置为空
ReactCurrentOwner.current = null;
/**
* before mutation阶段
*/
// 第一个阶段是 before mutation ,在这个阶段可以读取改变之前的的 state
// 生命周期函数 getSnapshotBeforeUpdate 的调用
// 进入commitBeforeMutationEffects
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
root,
finishedWork,
);
....
commitBeforeMutationEffects
export function commitBeforeMutationEffects(
root: FiberRoot,
firstChild: Fiber,
) {
// prepareForCommit() 返回null 并且初始化全局变量
focusedInstanceHandle = prepareForCommit(root.containerInfo);
nextEffect = firstChild;
// 开始处理副作用
commitBeforeMutationEffects_begin();
// 不再跟踪fiber节点
const shouldFire = shouldFireAfterActiveInstanceBlur;
shouldFireAfterActiveInstanceBlur = false;
focusedInstanceHandle = null;
return shouldFire;
}
commitBeforeMutationEffects_begin
function commitBeforeMutationEffects_begin() {
// 从上往下遍历,找到最底部并且有标记了 before mutation 的 fiber 节点
while (nextEffect !== null) {
const fiber = nextEffect;
//这个阶段只用于beforeActiveInstanceBlur.如果它是关闭的,就跳过
if (enableCreateEventHandleAPI) {
// 如果节点标记了deletions,将会被删除,并且
// 调用commitBeforeMutationEffectsDeletion创建 blur 事件并进行派发
const deletions = fiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const deletion = deletions[i];
commitBeforeMutationEffectsDeletion(deletion);
}
}
}
const child = fiber.child;
// 判断节点是否标记了 before mutation
if (
// 如果子树有包含BeforeMutationMask标记的节点
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
child !== null// child !== null判断是否是最底部节点
) {
// 向下继续获取
child.return = fiber;
nextEffect = child;
} else {
// 如果执行到这里,说明这个节点是标记了before mutation的`最底部节点`
// 执行complete,完成更新fiber节点的 props 和 state
commitBeforeMutationEffects_complete();
}
}
}
commitBeforeMutationEffects_complete
function commitBeforeMutationEffects_complete() {
// 从下至上,归并
while (nextEffect !== null) {
const fiber = nextEffect;
setCurrentDebugFiberInDEV(fiber);
try {
// 依次执行
commitBeforeMutationEffectsOnFiber(fiber);
} catch (error) {
captureCommitPhaseError(fiber, fiber.return, error);
}
resetCurrentDebugFiberInDEV();
// 判断是否存在兄弟节点
const sibling = fiber.sibling;
if (sibling !== null) {
sibling.return = fiber.return;
nextEffect = sibling;
return;
}
// 向上获取
nextEffect = fiber.return;
}
}
commitBeforeMutationEffectsOnFiber
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
const current = finishedWork.alternate;
const flags = finishedWork.flags;
if (enableCreateEventHandleAPI) {
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
// Check to see if the focused element was inside of a hidden (Suspense) subtree.
// TODO: Move this out of the hot path using a dedicated effect tag.
if (
finishedWork.tag === SuspenseComponent &&
isSuspenseBoundaryBeingHidden(current, finishedWork) &&
doesFiberContain(finishedWork, focusedInstanceHandle)
) {
shouldFireAfterActiveInstanceBlur = true;
beforeActiveInstanceBlur(finishedWork);
}
}
}
if ((flags & Snapshot) !== NoFlags) {
setCurrentDebugFiberInDEV(finishedWork);
// 根据 tag 的不同进入不同的处理逻辑
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
break;
}
case ....
.....
总结
可以看到在
before mutation时,其内部逻辑和render阶段的beginWork和completeWork是一样的,先递后归
记录学习过程,如有错误欢迎指出