Отладка исходного кода Reactсклад
Основной причиной взаимодействия с пользовательским интерфейсом являются различные события, а это значит, что события напрямую связаны с обновлениями. Обновления, созданные разными событиями, имеют разные приоритеты, поэтому корень приоритета обновления лежит в приоритете события. Генерация обновления может напрямую привести к тому, что React создаст задачу обновления, которая в конечном итоге будет запланирована Планировщиком.
Поэтому в React события искусственно разделены на уровни, а конечной целью является определение приоритета задач планирования, поэтому в React есть набор механизмов приоритета от событий до планирования.
В этой статье основное внимание будет уделено выявлению конверсионных отношений между приоритетом события, приоритетом обновления, приоритетом задачи и приоритетом планирования.
- Приоритет события: в зависимости от срочности взаимодействия пользовательских событий приоритет
- Приоритет обновления: приоритет (update.lane) объекта обновления (обновления), который событие вызвало React.
- Приоритет задачи: после создания объекта обновления React выполняет задачу обновления, приоритет которой принадлежит этой задаче.
- Приоритет планирования: планировщик создает задачу планирования на основе задачи обновления React и приоритета этой задачи планирования.
Первые три относятся к механизму приоритетов React, а четвертый относится к механизму приоритетов Scheduler.Scheduler имеет свой собственный механизм приоритетов.Хотя он отличается от React, разделение уровней в основном такое же. Начнем с приоритета события.
Начальная точка приоритета: приоритет события
React делит события на три уровня в зависимости от их срочности:
- Дискретное событие (DiscreteEvent): щелчок, нажатие клавиши, фокусировка и т. д. Запуск этих событий не является непрерывным, а приоритет равен 0.
- User BlockingEvent: перетаскивание, прокрутка, наведение мыши и т. д., которые характеризуются непрерывным срабатыванием, блокировкой рендеринга и приоритетом 1.
- ContinuousEvent: обновление времени и возможность воспроизведения тегов canplay, error и audio с наивысшим приоритетом 2.
приоритет события отправки
Приоритет события определяется на этапе регистрации.При регистрации события с корнем создаются обработчики событий с разными приоритетами в зависимости от категории события и, наконец, привязываются к корню.
let listener = createEventListenerWrapperWithPriority(
targetContainer,
domEventName,
eventSystemFlags,
listenerPriority,
);
createEventListenerWrapperWithPriorityНазвание функции уже объясняет, что именно она делает. Сначала он найдет соответствующий приоритет события в соответствии с именем события, а затем вернет различные функции прослушивателя событий в соответствии с приоритетом.
export function createEventListenerWrapperWithPriority(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
priority?: EventPriority,
): Function {
const eventPriority =
priority === undefined
? getEventPriorityForPluginSystem(domEventName)
: priority;
let listenerWrapper;
switch (eventPriority) {
case DiscreteEvent:
listenerWrapper = dispatchDiscreteEvent;
break;
case UserBlockingEvent:
listenerWrapper = dispatchUserBlockingUpdate;
break;
case ContinuousEvent:
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
Прослушиватель событий, окончательно привязанный к корню, на самом делеdispatchDiscreteEvent,dispatchUserBlockingUpdate,dispatchEventодин из этих трех. Все они делают одно и то же, выполняя настоящие обработчики событий с соответствующими приоритетами событий.
Например:dispatchDiscreteEventа такжеdispatchUserBlockingUpdateВ конечном итоге обработчик события будет выполняться на уровне события UserBlockingEvent.
Для выполнения функции обработчика событий с определенным приоритетом фактически требуется помощь планировщикаrunWithPriorityфункция для реализации:
function dispatchUserBlockingUpdate(
domEventName,
eventSystemFlags,
container,
nativeEvent,
) {
...
runWithPriority(
UserBlockingPriority,
dispatchEvent.bind(
null,
domEventName,
eventSystemFlags,
container,
nativeEvent,
),
);
...
}
Это может записать приоритет события в Планировщике, что эквивалентно указанию Планировщику: Помогите мне записать приоритет текущей отправки события и получить его непосредственно от вас, когда React создаст объект обновления (т.е. обновление) для расчета обновления. приоритет Достаточно.
function unstable_runWithPriority(priorityLevel, eventHandler) {
switch (priorityLevel) {
case ImmediatePriority:
case UserBlockingPriority:
case NormalPriority:
case LowPriority:
case IdlePriority:
break;
default:
priorityLevel = NormalPriority;
}
var previousPriorityLevel = currentPriorityLevel;
// 记录优先级到Scheduler内部的变量里
currentPriorityLevel = priorityLevel;
try {
return eventHandler();
} finally {
currentPriorityLevel = previousPriorityLevel;
}
}
приоритет обновления
Взяв за пример setState, выполнение события приведет к выполнению setState, а setState по сути вызывает enqueueSetState для генерации объекта обновления, после чего будет рассчитан его приоритет обновления, а именно update.lane:
const classComponentUpdater = {
enqueueSetState(inst, payload, callback) {
...
// 依据事件优先级创建update的优先级
const lane = requestUpdateLane(fiber, suspenseConfig);
const update = createUpdate(eventTime, lane, suspenseConfig);
update.payload = payload;
enqueueUpdate(fiber, update);
// 开始调度
scheduleUpdateOnFiber(fiber, lane, eventTime);
...
},
};
ФокусrequestUpdateLane, он сначала находит приоритет, записанный в Scheduler: schedulerPriority, а затем вычисляет update priority: lane, конкретный процесс расчета находится в функции findUpdateLane, Процесс вычисления представляет собой операцию, которая занимает свободные биты в последовательности от старшего к младшему.здесь, который здесь подробно описываться не будет.
export function requestUpdateLane(
fiber: Fiber,
suspenseConfig: SuspenseConfig | null,
): Lane {
...
// 根据记录下的事件优先级,获取任务调度优先级
const schedulerPriority = getCurrentPriorityLevel();
let lane;
if (
(executionContext & DiscreteEventContext) !== NoContext &&
schedulerPriority === UserBlockingSchedulerPriority
) {
// 如果事件优先级是用户阻塞级别,则直接用InputDiscreteLanePriority去计算更新优先级
lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes);
} else {
// 依据事件的优先级去计算schedulerLanePriority
const schedulerLanePriority = schedulerPriorityToLanePriority(
schedulerPriority,
);
...
// 根据事件优先级计算得来的schedulerLanePriority,去计算更新优先级
lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
}
return lane;
}
getCurrentPriorityLevelОтвечает за чтение приоритета, записанного в планировщике:
function unstable_getCurrentPriorityLevel() {
return currentPriorityLevel;
}
После создания объекта обновления это означает, что страницу необходимо обновить, и будет вызвана функция scheduleUpdateOnFiber для ввода расписания. Перед началом фактического планирования будет рассчитан приоритет задачи обновления, сгенерированной на этот раз. Цель: к По сравнению с приоритетом существующих задач удобно принимать решения по планированию нескольких задач.
Логика планирования решений заключается вensureRootIsScheduledСреди функций это очень важная функция, которая контролирует вход задач React в Scheduler.
приоритет задачи
Обновление будет выполняться задачей обновления React. Приоритет задачи используется для определения срочности нескольких задач обновления. Он рассчитывается на основе приоритета обновления, например:
Предполагая, что есть два обновления, одно за другим, они сохраняют свои соответствующие приоритеты обновления и также будут выполняться соответствующими задачами обновления. После расчета приоритета, если приоритет задачи последнего выше приоритета задачи первого, Планировщик отменит планирование задачи первого; если приоритет задачи последнего равен приоритету задачи первого, Последнее не приведет к отмене первого, но будет повторно использовать задачи обновления первого, объединяя два обновления с одинаковым приоритетом в одну задачу; если приоритет задачи последнего ниже, чем приоритет задачи первого, то же самое не будет сделано. вызвать отмену предыдущей задачи, но после завершения предыдущего обновления, Снова используйте планировщик, чтобы инициировать планирование задачи для последнего.
В этом смысл приоритета задачи, который гарантирует своевременный ответ на высокоприоритетные задачи и объединяет планирование задач с одинаковым приоритетом.
Приоритет задачи рассчитывается, когда она должна быть запланирована, и код находится вensureRootIsScheduledВ функции:
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
...
// 获取nextLanes,顺便计算任务优先级
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
// 获取上面计算得出的任务优先级
const newCallbackPriority = returnNextLanesPriority();
...
}
При вызове getNextLanes для расчета пакета дорожек (nextLanes), которые должны быть обработаны в этом обновлении, getNextLanes вызовет getHighestPriorityLanes для расчета приоритета задачи. Принцип расчета приоритета задачи следующий: приоритет обновления (полоса обновления),
Он будет объединен с root.pendingLanes.После того, как root.pendingLanes будет обработан getNextLanes, он выберет те дорожки, которые должны быть обработаны, и передаст их вgetHighestPriorityLanes, согласно nextLanes, чтобы узнать приоритет этих полос как приоритет задачи.
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
...
// 都是这种比较赋值的过程,这里只保留两个以做简要说明
const inputDiscreteLanes = InputDiscreteLanes & lanes;
if (inputDiscreteLanes !== NoLanes) {
return_highestLanePriority = InputDiscreteLanePriority;
return inputDiscreteLanes;
}
if ((lanes & InputContinuousHydrationLane) !== NoLanes) {
return_highestLanePriority = InputContinuousHydrationLanePriority;
return InputContinuousHydrationLane;
}
...
return lanes;
}
getHighestPriorityLanesисходный кодВот, бериNextLanesисходный кодэто здесь
return_highestLanePriorityЭто приоритет задачи. Он имеет следующие значения. Чем больше значение, тем выше приоритет. В настоящее время следует понимать только роль приоритета задачи.
export const SyncLanePriority: LanePriority = 17;
export const SyncBatchedLanePriority: LanePriority = 16;
const InputDiscreteHydrationLanePriority: LanePriority = 15;
export const InputDiscreteLanePriority: LanePriority = 14;
const InputContinuousHydrationLanePriority: LanePriority = 13;
export const InputContinuousLanePriority: LanePriority = 12;
const DefaultHydrationLanePriority: LanePriority = 11;
export const DefaultLanePriority: LanePriority = 10;
const TransitionShortHydrationLanePriority: LanePriority = 9;
export const TransitionShortLanePriority: LanePriority = 8;
const TransitionLongHydrationLanePriority: LanePriority = 7;
export const TransitionLongLanePriority: LanePriority = 6;
const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;
Если задача обновления уже существует,ensureRootIsScheduledПосле получения приоритета новой задачи он сравнивает его с приоритетом старой задачи, чтобы принять решение о повторной инициации планирования. быть рассчитаны.
приоритет планирования
Как только задача будет запланирована, она войдет в планировщик.В планировщике задача будет упакована для создания задачи, принадлежащей самому планировщику.Приоритет, удерживаемый этой задачей, является приоритетом планирования.
Что оно делает? В Планировщике очередь просроченных задач и очередь неистекших задач используются для управления его внутренними задачами, а задачи в очереди просроченных задач основаны на Отсортируйте по сроку действия, и самые ранние просроченные будут перечислены первыми, чтобы их можно было обработать первыми. Время истечения рассчитывается по приоритету планирования, и время истечения, соответствующее разным приоритетам планирования, отличается.
Приоритет планирования задаетсяприоритет задачирассчитано, вensureRootIsScheduledКогда обновление действительно заставляет планировщик инициировать планирование, он вычисляет приоритет планирования.
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
...
// 根据任务优先级获取Scheduler的调度优先级
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority,
);
// 计算出调度优先级之后,开始让Scheduler调度React的更新任务
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
...
}
lanePriorityToSchedulerPriorityПроцесс вычисления приоритета планирования заключается в определении соответствующего приоритета планирования в соответствии с приоритетом задачи.
export function lanePriorityToSchedulerPriority(
lanePriority: LanePriority,
): ReactPriorityLevel {
switch (lanePriority) {
case SyncLanePriority:
case SyncBatchedLanePriority:
return ImmediateSchedulerPriority;
case InputDiscreteHydrationLanePriority:
case InputDiscreteLanePriority:
case InputContinuousHydrationLanePriority:
case InputContinuousLanePriority:
return UserBlockingSchedulerPriority;
case DefaultHydrationLanePriority:
case DefaultLanePriority:
case TransitionShortHydrationLanePriority:
case TransitionShortLanePriority:
case TransitionLongHydrationLanePriority:
case TransitionLongLanePriority:
case SelectiveHydrationLanePriority:
case RetryLanePriority:
return NormalSchedulerPriority;
case IdleHydrationLanePriority:
case IdleLanePriority:
case OffscreenLanePriority:
return IdleSchedulerPriority;
case NoLanePriority:
return NoSchedulerPriority;
default:
invariant(
false,
'Invalid update priority: %s. This is a bug in React.',
lanePriority,
);
}
}
Суммировать
В этой статье упоминаются в общей сложности 4 приоритета:Приоритет события, приоритет обновления, приоритет задачи, приоритет расписания, между ними существует прогрессивная связь. Приоритет события определяется самим событием, приоритет обновления рассчитывается событием, а затем помещается в root.pendingLanes, Приоритет задачи исходит из приоритета, соответствующего наиболее срочным дорожкам в root.pendingLanes, а приоритет планирования получается в соответствии с приоритетом задачи. Несколько приоритетов взаимосвязаны для обеспечения приоритетного выполнения высокоприоритетных задач.