React中的优先级管理是确保高效渲染的关键机制之一。React团队通过实现可中断渲染、时间切片(time slicing)和异步渲染(suspense)等技术,来优化组件的渲染性能。这些技术的核心在于优先级管理,即根据不同的任务类型和紧急程度,决定哪些任务应该优先执行。
一、优先级体系分类
- LanePriority(车道优先级)
- 基于 位掩码(Bitmask) 实现,通过二进制位标识不同优先级任务,如
SyncLane(同步任务)、InputContinuousLane(用户输入)等。 - 优先级数值越小级别越高,通过
&运算判断任务是否属于当前处理范围。
- 基于 位掩码(Bitmask) 实现,通过二进制位标识不同优先级任务,如
2. SchedulerPriority(调度优先级)
- 调度器(Scheduler)内部使用,与浏览器事件循环协作,定义
ImmediatePriority(立即执行)、UserBlockingPriority(用户阻塞)等5种级别。
- 任务过期时间(timeout)决定执行顺序,如 IMMEDIATE_PRIORITY_TIMEOUT 为 -1(立即执行)。
- ReactPriorityLevel(协调优先级)
- 用于协调阶段的优先级映射,如
DiscreteEventPriority(离散事件)对应SyncLane,确保事件响应与渲染逻辑匹配。
- 用于协调阶段的优先级映射,如
二、转换流程的三级体系
React 通过 事件类型 → Lane(车道) → 调度优先级 的三级体系实现优先级转换,核心逻辑如下:
- 事件类型映射为 Lane
- 用户交互(如点击、输入)触发事件时,React 根据事件类型确定其所属的 Lane。例如:
- 点击事件 →
SyncLane(同步车道,最高优先级) - 滚动事件 →
InputContinuousLane(连续输入车道,次高优先级)
- 点击事件 →
- Lane 使用 位掩码(Bitmask) 表示,通过二进制位运算快速合并或分离不同任务。
- 用户交互(如点击、输入)触发事件时,React 根据事件类型确定其所属的 Lane。例如:
- Lane 转换为调度优先级
- Lane 通过
getCurrentPriorityLevel函数映射为 SchedulerPriority(调度优先级)。例如:SyncLane→ImmediatePriority(立即执行);InputContinuousLane→UserBlockingPriority(用户阻塞级别)。
- 调度器根据该优先级分配任务过期时间(timeout),决定执行顺序。
- Lane 通过
- 协调阶段的 ReactPriorityLevel 映射
- 在协调阶段(Reconciliation),Lane 进一步映射为 ReactPriorityLevel,用于内部状态更新决策13。例如:
SyncLane→DiscreteEventPriority(离散事件优先级)。
- 在协调阶段(Reconciliation),Lane 进一步映射为 ReactPriorityLevel,用于内部状态更新决策13。例如:
三、底层源码实现关键函数
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;
}
四、动态调整与异常处理
- 高优先级插队
- 若低优先级任务执行中被高优先级任务中断,React 将缓存当前状态,优先处理高优先级任务,完成后恢复低优先级任务。
- 例如:滚动中触发点击事件,点击任务立即执行,滚动任务暂存后恢复。
- 饥饿问题处理
- 长时间未执行的低优先级任务会被提升为
RetryLane,防止永久延迟。
- 长时间未执行的低优先级任务会被提升为
五、总结
React 的优先级转换机制通过 多级映射 和 动态调整 实现高效任务调度:
- 事件驱动:用户交互类型直接决定 Lane 优先级;
- 位运算高效性:Lane 的位掩码设计支持快速合并、分离任务;
- 调度器协作:最终由调度器根据优先级分配执行时机,确保高优先级任务快速响应。