Fiber Schedule

/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;
}

你可能感兴趣的:(Fiber Schedule)