Render阶段
# Render阶段
在本节正式开始前,让我们复习下这一章到目前为止所学的。
Renderer
工作的阶段被称为commit
阶段。commit
阶段可以分为三个子阶段:
- before mutation阶段(执行
DOM
操作前) - mutation阶段(执行
DOM
操作) - layout阶段(执行
DOM
操作后)
本节我们看看before mutation阶段
(执行DOM
操作前)都做了什么。
# 概览
before mutation阶段
的代码很短,整个过程就是遍历effectList
并调用commitBeforeMutationEffects
函数处理。
这部分源码在这里 (opens new window) (opens new window)。为了增加可读性,示例代码中删除了不相关的逻辑
// 保存之前的优先级,以同步优先级执行,执行完毕后恢复之前优先级
const previousLanePriority = getCurrentUpdateLanePriority();
setCurrentUpdateLanePriority(SyncLanePriority);
// 将当前上下文标记为CommitContext,作为commit阶段的标志
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
// 处理focus状态
focusedInstanceHandle = prepareForCommit(root.containerInfo);
shouldFireAfterActiveInstanceBlur = false;
// beforeMutation阶段的主函数
commitBeforeMutationEffects(finishedWork);
focusedInstanceHandle = null;
我们重点关注beforeMutation
阶段的主函数commitBeforeMutationEffects
做了什么。
# commitBeforeMutationEffects
大体代码逻辑:
function commitBeforeMutationEffects() {
while (nextEffect !== null) {
const current = nextEffect.alternate;
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
// ...focus blur相关
}
const effectTag = nextEffect.effectTag;
// 调用getSnapshotBeforeUpdate
if ((effectTag & Snapshot) !== NoEffect) {
commitBeforeMutationEffectOnFiber(current, nextEffect);
}
// 调度useEffect
if ((effectTag & Passive) !== NoEffect) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
nextEffect = nextEffect.nextEffect;
}
}
整体可以分为三部分:
- 处理
DOM节点
渲染/删除后的autoFocus
、blur
逻辑。 - 调用
getSnapshotBeforeUpdate
生命周期钩子。 - 调度
useEffect
。
我们讲解下2、3两点。
# 调用getSnapshotBeforeUpdate
commitBeforeMutationEffectOnFiber
是commitBeforeMutationLifeCycles
的别名。
在该方法内会调用getSnapshotBeforeUpdate
。
你可以在这里 (opens new window) (opens new window)看到这段逻辑
从React
v16开始,componentWillXXX
钩子前增加了UNSAFE_
前缀。
究其原因,是因为Stack Reconciler
重构为Fiber Reconciler
后,render阶段
的任务可能中断/重新开始,对应的组件在render阶段
的生命周期钩子(即componentWillXXX
)可能触发多次。
这种行为和React
v15不一致,所以标记为UNSAFE_
。
为此,React
提供了替代的生命周期钩子getSnapshotBeforeUpdate
。
我们可以看见,getSnapshotBeforeUpdate
是在commit阶段
内的before mutation阶段
调用的,由于commit阶段
是同步的,所以不会遇到多次调用的问题。