lane 优先级
在 React lanes 模型是 31 个二进制通道。以 1 的位置表示当前更新的优先级,全为 0 时表示没有更新。优先级从右往左依次降低。
如下图所示:


事件优先级
事件优先级有以下几种:
ini
export const DiscreteEventPriority = SyncLane;
export const ContinuousEventPriority = InputContinuousLane;
export const DefaultEventPriority = DefaultLane;
export const IdleEventPriority = IdleLane;
可以看到,事件优先级的值其实就是对应的 lane 优先级。由于 react 是合成事件,所以在触发事件时,会调用 setCurrentUpdatePriority
函数,将优先级设置为当前事件对应优先级,方便调用。
js
export function getEventPriority(domEventName: DOMEventName): * {
switch (domEventName) {
// 删除其他事件
// ...
case 'click':
return DiscreteEventPriority;
case 'drag':
return ContinuousEventPriority;
default:
return DefaultEventPriority;
}
}
getEventPriority
函数根据事件名返回不同的事件优先级。
这个函数在创建事件监听函数时会调用,获取当前事件对应的事件优先级。
ini
export function createEventListenerWrapperWithPriority(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
): Function {
const eventPriority = getEventPriority(domEventName);
let listenerWrapper;
switch (eventPriority) {
case DiscreteEventPriority:
listenerWrapper = dispatchDiscreteEvent;
break;
case ContinuousEventPriority:
listenerWrapper = dispatchContinuousEvent;
break;
case DefaultEventPriority:
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
function dispatchDiscreteEvent(
domEventName,
eventSystemFlags,
container,
nativeEvent,
) {
const previousPriority = getCurrentUpdatePriority();
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = null;
try {
setCurrentUpdatePriority(DiscreteEventPriority);
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
}
}
需要注意的是 getCurrentUpdatePriority
和 setCurrentUpdatePriority
,这两个函数操作的一个全局变量 currentUpdatePriority
。这个变量缓存了当前更新的优先级。
在调用 setState
(这里举个例子,其实不止这个函数) 函数时,就可以调用 requestUpdateLane
获取当前优先级。
js
export function requestUpdateLane(fiber: Fiber): Lane {
// ...
const updateLane: Lane = (getCurrentUpdatePriority(): any);
if (updateLane !== NoLane) {
return updateLane;
}
const eventLane: Lane = (getCurrentEventPriority(): any);
return eventLane;
}
requestUpdateLane
先从 getCurrentUpdatePriority
函数获取优先级,如果存在则直接返回,不存在则返回事件优先级。
最后在创建更新时,就有了优先级,于是创建的更新队列也就有了优先级。
如图所示:

这是加了优先级之后的更新链表。为了实现高优先级先执行,还需要维护一个链表。这个链表是实际执行的链表。为此需要在更新队列对象中增加几个参数。
js
function initializeUpdateQueue(fiber) {
const queue = {
baseState: fiber.memoizedState, //本次更新前该Fiber节点的state,Update基于该state计算更新后的state
firstBaseUpdate: null, //本次更新前该Fiber节点已保存的Update链表头
lastBaseUpdate: null, //本次更新前该Fiber节点已保存的Update链表尾
shared: {
//触发更新时,产生的Update会保存在shared.pending中形成单向环状链表
//当由Update计算state时这个环会被剪开并连接在lastBaseUpdate后面
pending: null,
},
};
fiber.updateQueue = queue;
}
老更新队列为空示例图:
老更新队列不为空示意图:

由上图所示,当更新队列执行时,实际执行的队列为 firstBaseUpdate
开头到 lastBaseUpdate
结束的更新队列。
当更新优先级低于渲染优先级时,该更新会被跳过,并将 firstBaseUpdate
指向该更新,继续遍历链表,将 lastBaseUpdate
指向链表尾部。
在下一次更新时,先合并链表,再根据优先级执行。
源码在 /Users/sunxianfu/Desktop/Project/react/packages/react-reconciler/src/ReactFiberClassUpdateQueue.old.js
。