七、咦?React 源码优先级管理机制原来是这样子滴

213 阅读4分钟

React中的优先级管理是确保高效渲染的关键机制之一。React团队通过实现可中断渲染、时间切片(time slicing)和异步渲染(suspense)等技术,来优化组件的渲染性能。这些技术的核心在于优先级管理,即根据不同的任务类型和紧急程度,决定哪些任务应该优先执行。

一、优先级体系分类

  1. LanePriority(车道优先级)
    • 基于 位掩码(Bitmask) 实现,通过二进制位标识不同优先级任务,如 SyncLane(同步任务)、InputContinuousLane(用户输入)等‌。
    • 优先级数值越小级别越高,通过 & 运算判断任务是否属于当前处理范围‌。

image.png 2. SchedulerPriority(调度优先级) ‌ - 调度器(Scheduler)内部使用,与浏览器事件循环协作,定义 ImmediatePriority(立即执行)、UserBlockingPriority(用户阻塞)等5种级别‌。 - 任务过期时间(timeout)决定执行顺序,如 IMMEDIATE_PRIORITY_TIMEOUT 为 -1(立即执行)‌。

image.png

  1. ReactPriorityLevel(协调优先级)
    • 用于协调阶段的优先级映射,如 DiscreteEventPriority(离散事件)对应 SyncLane,确保事件响应与渲染逻辑匹配‌。

image.png

二、转换流程的三级体系

React 通过 事件类型 → Lane(车道) → 调度优先级 的三级体系实现优先级转换,核心逻辑如下:

  1. 事件类型映射为 Lane
    • 用户交互(如点击、输入)触发事件时,React 根据事件类型确定其所属的 Lane。例如:
      • 点击事件 → SyncLane(同步车道,最高优先级)
      • 滚动事件 → InputContinuousLane(连续输入车道,次高优先级)‌
    • Lane 使用 位掩码(Bitmask) 表示,通过二进制位运算快速合并或分离不同任务‌。
  2. Lane 转换为调度优先级
    • Lane 通过 getCurrentPriorityLevel 函数映射为 SchedulerPriority(调度优先级)‌。例如:
      • SyncLane → ImmediatePriority(立即执行);
      • InputContinuousLane → UserBlockingPriority(用户阻塞级别)‌。
    • 调度器根据该优先级分配任务过期时间(timeout),决定执行顺序‌。
  3. 协调阶段的 ReactPriorityLevel 映射
    • 在协调阶段(Reconciliation),Lane 进一步映射为 ReactPriorityLevel,用于内部状态更新决策‌13。例如:
      • SyncLane → DiscreteEventPriority(离散事件优先级)‌。

三、底层源码实现关键函数

1. 事件到 Lane 的转换

export function getEventPriority(domEventName: DOMEventName): EventPriority {
  switch (domEventName) {
    // Used by SimpleEventPlugin:
    case 'beforetoggle':
    case 'cancel':
    case 'click':
    case 'close':
    case 'contextmenu':
    case 'copy':
    case 'cut':
    case 'auxclick':
    case 'dblclick':
    case 'dragend':
    case 'dragstart':
    case 'drop':
    case 'focusin':
    case 'focusout':
    case 'input':
    case 'invalid':
    case 'keydown':
    case 'keypress':
    case 'keyup':
    case 'mousedown':
    case 'mouseup':
    case 'paste':
    case 'pause':
    case 'play':
    case 'pointercancel':
    case 'pointerdown':
    case 'pointerup':
    case 'ratechange':
    case 'reset':
    case 'resize':
    case 'seeked':
    case 'submit':
    case 'toggle':
    case 'touchcancel':
    case 'touchend':
    case 'touchstart':
    case 'volumechange':
    // Used by polyfills: (fall through)
    case 'change':
    case 'selectionchange':
    case 'textInput':
    case 'compositionstart':
    case 'compositionend':
    case 'compositionupdate':
    // Only enableCreateEventHandleAPI: (fall through)
    case 'beforeblur':
    case 'afterblur':
    // Not used by React but could be by user code: (fall through)
    case 'beforeinput':
    case 'blur':
    case 'fullscreenchange':
    case 'focus':
    case 'hashchange':
    case 'popstate':
    case 'select':
    case 'selectstart':
      return DiscreteEventPriority;
    case 'drag':
    case 'dragenter':
    case 'dragexit':
    case 'dragleave':
    case 'dragover':
    case 'mousemove':
    case 'mouseout':
    case 'mouseover':
    case 'pointermove':
    case 'pointerout':
    case 'pointerover':
    case 'scroll':
    case 'touchmove':
    case 'wheel':
    // Not used by React but could be by user code: (fall through)
    case 'mouseenter':
    case 'mouseleave':
    case 'pointerenter':
    case 'pointerleave':
      return ContinuousEventPriority;
    case 'message': {
      // We might be in the Scheduler callback.
      // Eventually this mechanism will be replaced by a check
      // of the current priority on the native scheduler.
      const schedulerPriority = getCurrentSchedulerPriorityLevel();
      switch (schedulerPriority) {
        case ImmediateSchedulerPriority:
          return DiscreteEventPriority;
        case UserBlockingSchedulerPriority:
          return ContinuousEventPriority;
        case NormalSchedulerPriority:
        case LowSchedulerPriority:
          // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
          return DefaultEventPriority;
        case IdleSchedulerPriority:
          return IdleEventPriority;
        default:
          return DefaultEventPriority;
      }
    }
    default:
      return DefaultEventPriority;
  }
}

2. Lane 到调度优先级的转换

export function lanesToEventPriority(lanes: Lanes): EventPriority {
    const lane = getHighestPriorityLane(lanes);
    if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
    return DiscreteEventPriority;
    }
    if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
        return ContinuousEventPriority;
    }
    if (includesNonIdleWork(lane)) {
        return DefaultEventPriority;
    }
    return IdleEventPriority;
}

‌3. 任务调度入口

function unstable_scheduleCallback(
  priorityLevel: PriorityLevel,
  callback: Callback,
  options?: {delay: number},
): Task {
  var currentTime = getCurrentTime();

  var startTime;
  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }

  var timeout;
  switch (priorityLevel) {
    case ImmediatePriority:
      // Times out immediately
      timeout = -1;
      break;
    case UserBlockingPriority:
      // Eventually times out
      timeout = userBlockingPriorityTimeout;
      break;
    case IdlePriority:
      // Never times out
      timeout = maxSigned31BitInt;
      break;
    case LowPriority:
      // Eventually times out
      timeout = lowPriorityTimeout;
      break;
    case NormalPriority:
    default:
      // Eventually times out
      timeout = normalPriorityTimeout;
      break;
  }

  var expirationTime = startTime + timeout;

  var newTask: Task = {
    id: taskIdCounter++,
    callback,
    priorityLevel,
    startTime,
    expirationTime,
    sortIndex: -1,
  };
  if (enableProfiling) {
    newTask.isQueued = false;
  }

  if (startTime > currentTime) {
    // This is a delayed task.
    newTask.sortIndex = startTime;
    push(timerQueue, newTask);
    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      // All tasks are delayed, and this is the task with the earliest delay.
      if (isHostTimeoutScheduled) {
        // Cancel an existing timeout.
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      // Schedule a timeout.
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    newTask.sortIndex = expirationTime;
    push(taskQueue, newTask);
    if (enableProfiling) {
      markTaskStart(newTask, currentTime);
      newTask.isQueued = true;
    }
    // Schedule a host callback, if needed. If we're already performing work,
    // wait until the next time we yield.
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback();
    }
  }

  return newTask;
}

四、动态调整与异常处理

  1. 高优先级插队
    • 若低优先级任务执行中被高优先级任务中断,React 将缓存当前状态,优先处理高优先级任务,完成后恢复低优先级任务‌。
    • 例如:滚动中触发点击事件,点击任务立即执行,滚动任务暂存后恢复‌。
  2. 饥饿问题处理
    • 长时间未执行的低优先级任务会被提升为 RetryLane,防止永久延迟‌。

五、总结

React 的优先级转换机制通过 多级映射 和 动态调整 实现高效任务调度:

  1. 事件驱动:用户交互类型直接决定 Lane 优先级;
  2. 位运算高效性:Lane 的位掩码设计支持快速合并、分离任务‌;
  3. 调度器协作:最终由调度器根据优先级分配执行时机,确保高优先级任务快速响应‌。