/packages/react-reconciler/src/ReactFiberScheduler.js
概念
expirationTime
- 用来定义更新任务的优先级。expirationTime 越大优先级越高,初始值为 NoWork = 0。
- expirationTime 是相对于调度器初始调用的起始时间而言的一个时间段;调度器初始调用后的某一段时间内,需要调度完成这项更新,这个时间段长度值就是到期时间值。
- React 中有两种类型的 expirationTime,一个是 Interactive 的,比如说是由事件触发的,那么他的响应优先级会比较高因为涉及到交互。另一种是异步,优先级相对较低。
alternate
可以理解为一个 fiber 版本池,用于交替记录组件更新(切分任务后变成多阶段更新)过程中 fiber 的更新,因为在组件更新的各阶段,更新前及更新过程中 fiber 状态并不一致,在需要恢复时(如,发生冲突),即可使用另一者直接回退至上一版本 fiber。
- 使用 alternate 属性双向连接一个当前 fiber 和其 work-in-progress,当前 fiber.alternate 指向其 work-in-progress,work-in-progress.alternate 指向当前稳定fiber;
- 当前 fiber 的替换版本是其 work-in-progress,work-in-progress 的交替版本是当前 fiber;
- 当 work-in-progress 更新一次后,将同步至当前 fiber,然后继续处理,同步直至任务完成;
- work-in-progress 指向处理过程中的fiber,而当前 fiber 总是维护处理完成的最新版本的 fiber。
源码
function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
const root = scheduleWorkToRoot(fiber, expirationTime);
// isWorking 代表正在进行渲染
// nextRenderExpirationTime 是全局变量
// nextRenderExpirationTime 在是新的 renderRoot 的时候会被设置为当前任务的expirationTime
// 一旦他被设置,只有当下次任务是 NoWork 的时候他才会被再次设置为 NoWork,初始值也是 NoWork
if (
!isWorking &&
nextRenderExpirationTime !== NoWork &&
expirationTime > nextRenderExpirationTime
) {
// This is an interruption. (Used for performance tracking.)
// 当前新的高优先级的任务打断了老的低优先级任务的执行
// interruptedBy 主要是用于告知 devtool 任务在哪里被打断了,没有实际用途
interruptedBy = fiber;
resetStack();
}
// root.expirationTime 与 入参的 expirationTime 不一定相等
markPendingPriorityLevel(root, expirationTime);
if (
// If we're in the render phase, we don't need to schedule this root
// for an update, because we'll do it before we exit...
// isWorking 包含 render 和 commit 阶段
!isWorking ||
// 说明上一次的更新已经结束了
// commit 阶段(把更新之后的 Fiber 转为真实 DOM 的更新)不可打断,render 可以?
// isCommitting 只有在 commitRoot 函数中某个阶段会被设置为true,最终还是会被设置为 false
isCommitting ||
// ...unless this is a different root than the one we're rendering.
// 对于单个 root 而言,nextRoot === root,因此一般情况无需考虑这个
nextRoot !== root
) {
// expirationTime 和 nextExpirationTimeToWorkOn
const rootExpirationTime = root.expirationTime;
requestWork(root, rootExpirationTime);
}
// 用来防止组件更新流程中出现死循环
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
// Reset this back to zero so subsequent updates don't throw.
nestedUpdateCount = 0;
}
}
根据 Fiber 节点查找对应的 root,并且在找的过程中更新节点的 expirationTime
function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
recordScheduleUpdate();
// Update the source fiber's expiration time
if (fiber.expirationTime < expirationTime) {
// fiber 为当前产生更新的组件对应的 Fiber
// fiber.expirationTime 初始值为 NoWork = 0
// 设置为优先级更高的 expirationTime,expirationTime 越大优先级越高
// 16.6之前是越小越高
// 更新完成之后会移除 expirationTime,expirationTime
fiber.expirationTime = expirationTime;
}
// 更新 alternate 的 expirationTime
let alternate = fiber.alternate;
if (alternate !== null && alternate.expirationTime < expirationTime) {
alternate.expirationTime = expirationTime;
}
// Walk the parent path to the root and update the child expiration time.
// 以下代码是遍历父节点直到root,并且依次更新子节点的 expirationTime,使其保留优先级最高的 expirationTime
// node 就是父节点
let node = fiber.return;
let root = null;
// 只有 root 的父节点为 null,HostRoot 就是 RootFiber
if (node === null && fiber.tag === HostRoot) {
root = fiber.stateNode; // 就是 FiberRoot
} else {
while (node !== null) {
alternate = node.alternate;
// expirationTime 是当前 Fiber 更新产生的 expirationTime
// childExpirationTime 是 node 子树中优先级最高的(最大的)expirationTime
if (node.childExpirationTime < expirationTime) {
node.childExpirationTime = expirationTime;
if (
// alternate 是 node 的备份
alternate !== null &&
alternate.childExpirationTime < expirationTime
) {
alternate.childExpirationTime = expirationTime;
}
} else if (
alternate !== null &&
alternate.childExpirationTime < expirationTime
) {
alternate.childExpirationTime = expirationTime;
}
if (node.return === null && node.tag === HostRoot) {
root = node.stateNode;
break;
}
node = node.return;
}
}
return root;
}
状态回退到打断之前,即没有更新的状态
function resetStack() {
// nextUnitOfWork 是下一个需要进行更新的节点
if (nextUnitOfWork !== null) {
// nextUnitOfWork 不为空说明之前执行的是异步任务,由于时间片不够,把执行权交还给了浏览器,更新中途被打断
let interruptedWork = nextUnitOfWork.return;
while (interruptedWork !== null) {
// 状态回退到打断之前,即没有更新的状态
unwindInterruptedWork(interruptedWork);
interruptedWork = interruptedWork.return;
}
}
// 重置变量值
nextRoot = null;
nextRenderExpirationTime = NoWork;
nextLatestAbsoluteTimeoutMs = -1;
nextRenderDidError = false;
nextUnitOfWork = null;
}