记录学习过程,如有错误欢迎指出
开始之前(必看)
React version 18.2.0
DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知
我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)
因为使用了深度优先的方式讲解,
耦合比较重,不建议跳着看
commitRootImpl(未完)
...
/**
* mutation阶段
*/
// mutation 阶段,可以在这个阶段 改变 host tree
// mutation 阶段并`不会`使用向下查找,向上归并的方式执行,而是直接执行commitMutationEffectsOnFiber函数
commitMutationEffects(root, finishedWork, lanes);
if (enableCreateEventHandleAPI) {
if (shouldFireAfterActiveInstanceBlur) {
afterActiveInstanceBlur();
}
}
resetAfterCommit(root.containerInfo);
...
commitMutationEffects(mutation阶段)
export function commitMutationEffects(
root: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
) {
// 优先级相关
inProgressLanes = committedLanes;
inProgressRoot = root;
setCurrentDebugFiberInDEV(finishedWork);
// 进入执行
commitMutationEffectsOnFiber(finishedWork, root, committedLanes);
setCurrentDebugFiberInDEV(finishedWork);
// 重置
inProgressLanes = null;
inProgressRoot = null;
}
commitMutationEffectsOnFiber
function commitMutationEffectsOnFiber(
finishedWork: Fiber,
root: FiberRoot,
lanes: Lanes,
) {
const current = finishedWork.alternate;
const flags = finishedWork.flags;
// The effect flag should be checked *after* we refine the type of fiber,
// because the fiber tag is more specific. An exception is any flag related
// to reconcilation, because those can be set on all fiber types.
// 根据tag判断
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
//其余tag判断
....
commitRootImpl(未完)
...
/**
* 重点:
* root.current = finishedWork;
* 交换页面指向的树
*/
/*
当 workInProgressFiber 树完成了渲染,就会将 current 指针
从 current Fiber 树指向 workInProgress Fiber 树,也就是这行代码所做的工作
*/
// 交换 workInProgress
root.current = finishedWork;
/**
* 为什么要在 mutation 阶段结束后,layout 阶段之前执行呢?
*
* 这是因为componentWillUnmount这个构造,绘制mutation时执行,可能会操作原来Fiber上的内容
* **为了保证数据的可靠性不会修改current指向**
*
* 而layout阶段会执行componentDidMount 和 componentDidUpdate钩子,此时需要获取到的 DOM 是更新后的
*/
/**
* layout阶段
*/
// 执行 layout
commitLayoutEffects(finishedWork, root, lanes);
// 告诉Scheduler在该帧的末尾让步,这样浏览器就有绘画的机会
requestPaint();
// 重置执行栈环境
executionContext = prevExecutionContext;
// 将优先级重置为之前的 非同步优先级,表示effect执行完毕了
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
// 没有effect的情况
} else {
// 没有effect还是会将current指向workInProgress
// 因为没有effect,所以也不会发生什么钩子的触发,所以这里改变current指向即可
root.current = finishedWork;
// Measure these anyway so the flamegraph explicitly shows that there were
// no effects.
// TODO: Maybe there's a better way to report this.
if (enableProfilerTimer) {
recordCommitTime();
}
}
...
commitLayoutEffects
export function commitLayoutEffects(
finishedWork: Fiber,
root: FiberRoot,
committedLanes: Lanes,
): void {
inProgressLanes = committedLanes;
inProgressRoot = root;
const current = finishedWork.alternate;
//执行
commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);
inProgressLanes = null;
inProgressRoot = null;
}
commitLayoutEffectOnFiber
function commitLayoutEffectOnFiber(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
// 在更新这个函数时,也要更新reappearLayoutEffects,它的作用是
// 把不在屏幕上显示的那颗树 从隐藏 => 可见时
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
commitRootImpl
....
const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
// 在前面rootDoesHavePassiveEffects被置为true,这里成立
if (rootDoesHavePassiveEffects) {
// This commit has passive effects. Stash a reference to them. But don't
// schedule a callback until after flushing layout work.
// 又置为false
rootDoesHavePassiveEffects = false;
//赋值rootWithPendingPassiveEffects,那么下次执行该函数时在该函数开头的do while就可以执行了
rootWithPendingPassiveEffects = root;
pendingPassiveEffectsLanes = lanes;
} else {
// 没有任何被动的影响,所以我们可以立即释放这个缓存的池
releaseRootPooledCache(root, remainingLanes);
}
// 再次读取一下pendingLanes,防止有effect在中途出现了
remainingLanes = root.pendingLanes;
if (remainingLanes === NoLanes) {
// 如果没有剩余的工作,我们可以清除已经失败的一组错误的边界
legacyErrorBoundariesThatAlreadyFailed = null;
}
onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);
// commit 阶段结尾,可能会在 commit 阶段产生新的更新,因此在 commit 阶段的结尾会重新调度一次
ensureRootIsScheduled(root, now());
// 判断recoverableErrors是否存在,如果存在说明出现了错误
if (recoverableErrors !== null) {
// 在这次渲染过程中出现了一些错误,但从这些错误中恢复过来,不需要把它浮现在用户界面上。我们在这里记录它们
const onRecoverableError = root.onRecoverableError;
for (let i = 0; i < recoverableErrors.length; i++) {
const recoverableError = recoverableErrors[i];
const componentStack = recoverableError.stack;
const digest = recoverableError.digest;
onRecoverableError(recoverableError.value, {componentStack, digest});
}
}
if (hasUncaughtError) {
hasUncaughtError = false;
const error = firstUncaughtError;
firstUncaughtError = null;
throw error;
}
// 如果被动效果是离散渲染的结果,就在当前任务结束时同步冲刷它们,这样就可以立即观察到结果。
// 否则(不是离散渲染),我们假设它们不受顺序(异步)的影响,不需要被外部系统观察到,所以我们可以等到绘制之后再进行
if (
includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
root.tag !== LegacyRoot
) {
flushPassiveEffects();
}
// 再读一下这个,因为一个被动效果可能已经更新了
remainingLanes = root.pendingLanes;
if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
markNestedUpdateScheduled();
}
if (root === rootWithNestedUpdates) {
nestedUpdateCount++;
} else {
nestedUpdateCount = 0;
rootWithNestedUpdates = root;
}
} else {
nestedUpdateCount = 0;
}
// react 中会将同步任务放在 flushSync 队列中,执行这个函数会执行它里面的同步任务
// 默认 react 中开启的是 legacy 模式,这种模式下的更新都是 同步的 更新,
// 未来会开启 concurrent 模式(并发模式),会出现不同优先级的更新
flushSyncCallbacks();
return null;
}
总结
我们现在知道了commite时内部会执行三个子节点,并且都会对不同的节点进行相应处理,因为react源码内有很多都涉及到这种逻辑,所以我单独写一篇文章来讲解
记录学习过程,如有错误欢迎指出