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;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容