先来看个全局变量 executionContext
。
executionContext
executionContext
标记当前执行环境,比如当前是 render 阶段或者 commit 阶段。
类型是 number
,取值有 4 种:
js
type ExecutionContext = number;
export const NoContext = /* */ 0b000;
const BatchedContext = /* 批量/同步更新 */ 0b001;
export const RenderContext = /* render 阶段 */ 0b010;
export const CommitContext = /* commit 阶段 */ 0b100;
初始值为 NoContext
:
js
// 参考 react/packages/react-reconciler/src/ReactFiberWorkLoop.js
let executionContext: ExecutionContext = NoContext;
两个阶段
和 render 阶段平行的还有个 commit 阶段。
- render 阶段,构建 fiber 树 VDOM
- commit 阶段,将 VDOM 转换成 DOM
两者入口如下:
js
// 这是每个并发任务的入口点,即通过 Scheduler 的所有内容
function performConcurrentWorkOnRoot(
root: FiberRoot,
didTimeout: boolean,
): RenderTaskFn | null {
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
// 没有更新
if (lanes === NoLanes) {
// Defensive coding. This is never expected to happen.
return null;
}
// 在某些情况下,我们会禁用时间切片:如果 work 过长时间做计算(为了防止饥饿而将其视为"过期"的 work),或者我们处于默认启用同步更新模式。
const shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
(disableSchedulerTimeoutInWorkLoop || !didTimeout);
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes) // 使用时间切片
// 1. render, 构建fiber树VDOM(beginWork|completeWork)
: renderRootSync(root, lanes); // 不用时间切片
// 2. commit, VDOM->DOM
commitRoot(root);
}
renderRootSync
renderRootSync
是 render 阶段入口,包含 4 个步骤:
- render 阶段开始
- workInProgressTransitions 赋值
- 初始化
- 遍历构建 fiber 树
详细代码如下:
js
function renderRootSync(root: FiberRoot) {
// 1. render阶段开始
const prevExecutionContext = executionContext;
executionContext |= RenderContext; // 注意这里不能用等号,如果使用等号,会覆盖其他 execution,比如如果是批量更新,如果直接覆盖,批量更新就没有了
// 2. workInProgressTransitions 赋值
workInProgressTransitions = getTransitionsForLanes(root, lanes);
// 3. 初始化
prepareFreshStack(root, lanes);
// 4. 遍历构建 fiber 树
workLoopSync();
// 重置 Context 的相关值,入 currentlyRenderingFiber 等
resetContextDependencies();
// 4. render结束,重置 executionContext
executionContext = prevExecutionContext;
workInProgressRoot = null; // 当前正在工作的 root
workInProgressRootRenderLanes = NoLanes;
// 防止有新的更新,再遍历一边更新队列
finishQueueingConcurrentUpdates();
return workInProgressRootExitStatus;
}
function prepareFreshStack(root: FiberRoot): Fiber {
root.finishedWork = null;
workInProgressRoot = root; // FiberRoot
const rootWorkInProgress = createWorkInProgress(root.current, null); // Fiber
if (workInProgress === null) {
workInProgress = rootWorkInProgress; // Fiber
}
// 把 concurrentQueues 的内容添加到 fiber 的 queue 中,即给 fiber 的 lanes、childLanes 赋值
finishQueueingConcurrentUpdates();
return rootWorkInProgress;
}
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork: Fiber) {
const current = unitOfWork.alternate;
// 1. beginWork
// 更新当前 fiber,比如 props/state 更新,生命周期函数执行、Hooks 函数执行等。
// 返回下一个 fiber。
let next = beginWork(current, unitOfWork);
// 把 pendingProps 更新到 memoizedProps
unitOfWork.memoizedProps = unitOfWork.pendingProps;
// 1.1 执行自己
// 1.2 (协调,bailout)返回子节点
if (next === null) {
// 没有产生新的work
// 如果不再产生新的 work,那么当前 work 结束
// 2. completeWork,比如生成原生节点,放在 fiber 的 stateNode 属性上
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
// 当前 Fiber 为 null
ReactCurrentOwner.current = null
}
// 深度优先遍历,子节点、兄弟节点、叔叔节点、爷爷的兄弟节点...(王朝的故事)
function completeUnitOfWork(unitOfWork: Fiber) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
let next = completeWork(current, completedWork);
if (next !== null) {
workInProgress = next;
return;
}
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
completedWork = returnFiber as Fiber;
workInProgress = completedWork;
} while (completedWork !== null);
}