React源码-更新优先级

198 阅读3分钟

React中的更新优先级

1. 优先级分类

React 中的更新优先级分为事件优先级、更新优先级和调度优先级三种。其中,事件优先级和更新优先级是由 React 自身的内部机制决定的。

2. lane 模型

React 中的 Lane 模型是 React 内部优先级系统的核心部分。Lane 模型的目的是为了避免低优先级的更新被高优先级的更新所阻塞,从而保证应用的响应性和性能。Lane 模型通过将任务分为不同的更新类型和优先级,使得 React 能够对任务进行优先级管理和调度。

在 React 内部凡是涉及到优先级调度的地方,都会使用 unstable_runWithPriority。

首先将 Lane 模型的优先级转换为 React Event 的优先级,因为他们的值都是 Lane 值,所以只需要做个多对少的映射就好了。然后将 React Event 优先级映射到 scheduler 的优先级,再调用 scheduler 的调度函数scheduleCallback完成一次调度下发:

ensureRootIsScheduled 函数中,React 会判断当前任务是同步任务还是异步任务。如果是同步任务,React 会直接加入更新队列,否则将任务按照优先级进行调度。React 使用 Scheduler 模块来进行任务调度,其中 scheduleCallback 函数会将任务添加到任务最小堆中。在任务的过期时间到达后,React 会执行任务回调函数。

//离散事件优先级 click onchange
export const DiscreteEventPriority = SyncLane; //1
//连续事件的优先级 mousemove
export const ContinuousEventPriority = InputContinuousLane; //4
//默认事件车道
export const DefaultEventPriority = DefaultLane; //16
//空闲事件优先级
export const IdleEventPriority = IdleLane; //

//判断事件优先级是不是比lane优先级小,小则优先级高
export function isHigherEventPriority(eventPriority, lane) {
  return eventPriority !== 0 && eventPriority < lane;
}
// 将lane模型中的优先级转换为事件优先级
export function lanesToEventPriority(lanes) {
  //获取最高优先级的lane
  let lane = getHighestPriorityLane(lanes);
  
  if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
    return DiscreteEventPriority; //1
  }
  if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
    return ContinuousEventPriority; //4
  }
  if (includesNonIdleWork(lane)) {
    return DefaultEventPriority; //16
  }
  return IdleEventPriority; //
}

// 在ReactFiberWorkLoop文件中,判断是否是同步任务,异步时转换优先级
function ensureRootIsScheduled(root, currentTime) {
  // 获取根上的任务,将已经过期的标记为过期,
  // 获取当前优先级最高的任务,并获取优先级,和根上在执行的任务对比优先级,/如果新的优先级和老的优先级一样,则可以进行批量更新
  // 如果没有要执行的任务,返回

  /**
   * 省略部分代码
   * */

  // 如果是同步任务,加入更新队列
  //如果不是同步,就需要调度一个新的任务
  let schedulerPriorityLevel;
  switch (lanesToEventPriority(nextLanes)) {
    case DiscreteEventPriority:
      schedulerPriorityLevel = ImmediateSchedulerPriority;
      break;
    case ContinuousEventPriority:
      schedulerPriorityLevel = UserBlockingSchedulerPriority;
      break;
    case DefaultEventPriority:
      schedulerPriorityLevel = NormalSchedulerPriority;
      break;
    case IdleEventPriority:
      schedulerPriorityLevel = IdleSchedulerPriority;
      break;
    default:
      schedulerPriorityLevel = NormalSchedulerPriority;
      break;
  }
  newCallbackNode = Scheduler_scheduleCallback(
    schedulerPriorityLevel,
    performConcurrentWorkOnRoot.bind(null, root)//performConcurrentWorkOnRoot方法为beginWork后面继续这部分
  );
}
//Scheduler.js
export const scheduleCallback = Scheduler.unstable_scheduleCallback;

function scheduleCallback(priorityLevel, callback) {
  // 获取当前的时候
  const currentTime = getCurrentTime();
  // 此任务的开时间
  const startTime = currentTime;
  //超时时间
  let timeout;
  //根据优先级计算过期的时间
  switch (priorityLevel) {
    case ImmediatePriority:
      timeout = IMMEDIATE_PRIORITY_TIMEOUT; // -1
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT; // 250ms
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT; //1073741823
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT; //10000
      break;
    case NormalPriority:
    default: //5000
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }
  //计算此任务的过期时间
  const expirationTime = startTime + timeout;
  const newTask = {
    id: taskIdCounter++,
    callback, //回调函数或者说任务函数
    priorityLevel, //优先级别
    startTime, //任务的开始时间
    expirationTime, //任务的过期时间
    sortIndex: expirationTime, //排序依赖
  };
  //向任务最小堆里添加任务,排序的依据是过期时间
  push(taskQueue, newTask);
  //flushWork执行工作,刷新工作,执行任务
  requestHostCallback(workLoop);
  return newTask;
}

小结

总之,在 React 中,Lane 模型和 Scheduler 模块是实现高效更新机制的关键。它们通过对任务进行优先级管理和调度,使得 React 能够在保证应用响应性和性能的前提下,尽可能地将更新任务分散到多个帧中,从而提高应用的运行效率和用户体验。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第5天,点击查看活动详情