- Published on
优先级转换关系
- Authors

- Name
- 李丹秋
事件优先级
export function getEventPriority(domEventName: DOMEventName): * {
switch (domEventName) {
// Used by SimpleEventPlugin:
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 'touchcancel':
case 'touchend':
case 'touchstart':
case 'volumechange':
// Used by polyfills:
// eslint-disable-next-line no-fallthrough
case 'change':
case 'selectionchange':
case 'textInput':
case 'compositionstart':
case 'compositionend':
case 'compositionupdate':
// Only enableCreateEventHandleAPI:
// eslint-disable-next-line no-fallthrough
case 'beforeblur':
case 'afterblur':
// Not used by React but could be by user code:
// eslint-disable-next-line no-fallthrough
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 'toggle':
case 'touchmove':
case 'wheel':
// Not used by React but could be by user code:
// eslint-disable-next-line no-fallthrough
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;
}
}
可以看到,一些点击事件,输入事件等都是离散事件优先级(同步优先级),而一些鼠标移动事件,滚动事件等都是连续事件优先级。
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;
}
比较特殊的是message事件,这个事件的优先级是根据当前的调度器优先级来决定的,如果是ImmediateSchedulerPriority,那么就是离散事件优先级,如果是UserBlockingSchedulerPriority,那么就是连续事件优先级,其他的都是默认事件优先级。
事件优先级转换为调度优先级
var schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediatePriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdlePriority;
break;
default:
schedulerPriorityLevel = NormalPriority;
break;
}
// 返回的是一个Task对象
newCallbackNode = scheduleCallback$1(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
Lane转换为事件优先级
function lanesToEventPriority(lanes) {
var lane = getHighestPriorityLane(lanes);
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
return DiscreteEventPriority;
}
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
return ContinuousEventPriority;
}
if (includesNonIdleWork(lane)) {
return DefaultEventPriority;
}
return IdleEventPriority;
}
点击事件触发后
当我们点击按钮的时候,实际上并不是直接调用onClick,而是调用的是#root提前经过事件委托的那个函数,其实也就是这个dispatchDiscreteEvent函数,在这个函数中做了一些事情他会收集到这个onClick,然后调用它,并且在中间还做了这样一件事情:
function dispatchDiscreteEvent(
domEventName,
eventSystemFlags,
container,
nativeEvent
) {
var previousPriority = getCurrentUpdatePriority();
try {
setCurrentUpdatePriority(DiscreteEventPriority);
// 这个函数中包含执行onClick,并且是同步执行
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
setCurrentUpdatePriority(previousPriority);
}
}
可以看到,在执行onClick的时候,全局的 currentUpdatePriority 已经被设置成了对应的事件优先级,等事件执行完毕,再恢复成原来的优先级,因此在某个事件产生的更新如果去获取更新优先级的话,在不满足前两个判断的情况下,它必然会获取到对应的这个事件优先级。
但是有一些更新并不是由事件产生的,可能是由IO等异步操作产生的,它们的执行可能没有对应的事件,这个时候就走第4个判断,getCurrentEventPriority,我们来看看它的实现:
function getCurrentEventPriority() {
var currentEvent = window.event;
if (currentEvent === undefined) {
return DefaultEventPriority; // 命中这个
}
return getEventPriority(currentEvent.type);
}
由IO等异步操作产生的没有事件,因此返回的是 DefaultEventPriority ,它对应的就是DefaultLane,比如
const App = ()=> {
const [num , setNum] = useState(0)
useEffect(()=>{
fetchData().then(res => setNum(res))
} , [])
return null
}
上面的 setNum() 产生的就是一个无事件的更新,获取的更新优先级就是 DefaultLane
好了讲到这里不知道大家会不会有一个疑问,为什么这里要把事件优先级了再分一下更新优先级呢,已经有了事件优先级不够了吗,反正页面的所有交互都是用事件驱动的,每一种事件规定一种优先级,然后交给后面的流程不就完了吗? 实际上,最核心的原因是因为事件和更新并非一一对应,换句话说,在一个事件中有可能产生多种优先级的更新,你不信的话看看下面的代码: jsx复制代码
const App = () => {
const [num, setNum] = React.useState(0);
const [count, setCount] = React.useState(0);
return (
<div>
<h1 id="h1">{num}</h1>
<button onClick={() => {
setNum(num + 1) // SyncLane
React.startTransition(()=>{
setCount(count + 1) // TransitionLane
})
}}>
{count}
</button>
</div>
);
};
startTransition 可以将某个状态的改变降低优先级至过渡优先级,因此在这一个 onClick 就产生了两种不同优先级的更新,因此当UI发生了某个交互的时候,不能简单的将事件的优先级当作它的更新优先级,而是要再次根据细节判断一下以获取它准确的更新优先级 ,在真实的场景下,有可能存在更加复杂的情况,因此这样的判断显得非常有必要了,现在你知道为什么存在更新优先级了吧?
Lane优先级转换为React事件优先级
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;
}
React事件优先级转换为Scheduler优先级
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;
}
调度优先级
当 update 产生后,会根据 update 优先级以及当前未完成的工作进行计算,得出本次任务的渲染优先级。并根据渲染优先级计算出调度优先级。这里需要注意的是,由于调度器实际上是可以脱离 react 存在的模块,因此调度优先级和update/渲染优先级/事件优先级不同并不是 lane 优先级。需要通过以下的逻辑进行转换: js复制代码
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;
}
每次一个任务结束,调度器会选出优先级最高的任务进行调度。