UI产生交互的根本原因是各种事件,这也就意味着事件与更新有着直接关系。不同事件产生的更新,它们的优先级是有差异的,所以更新优先级的根源在于事件的优先级。一个更新的产生可直接导致React生成一个更新任务,最终这个任务被Scheduler调度。
所以在React中,人为地将事件划分了等级,最终目的是决定调度任务的轻重缓急,因此,React有一套从事件到调度的优先级机制。
本文将围绕事件优先级、更新优先级、任务优先级、调度优先级,重点梳理它们之间的转化关系。
事件优先级:按照用户事件的交互紧急程度,划分的优先级更新优先级:事件导致React产生的更新对象(update)的优先级(update.lane)任务优先级:产生更新对象之后,React去执行一个更新任务,这个任务所持有的优先级调度优先级:Scheduler依据React更新任务生成一个调度任务,这个调度任务所持有的优先级前三者属于React的优先级机制,第四个属于Scheduler的优先级机制,Scheduler内部有自己的优先级机制,虽然与React有所区别,但等级的划分基本一致。下面我们从事件优先级开始说起。
优先级的起点:事件优先级React按照事件的紧急程度,把它们划分成三个等级:
离散事件(DiscreteEvent):click、keydown、focusin等,这些事件的触发不是连续的,优先级为0。用户阻塞事件(UserBlockingEvent):drag、scroll、mouseover等,特点是连续触发,阻塞渲染,优先级为1。连续事件(ContinuousEvent):canplay、error、audio标签的timeupdate和canplay,优先级最高,为2。事件优先级的Map派发事件优先级事件优先级是在注册阶段被确定的,在向root上注册事件时,会根据事件的类别,创建不同优先级的事件监听(listener),最终将它绑定到root上去。
letlistener=createEventListenerWrapperWithPriority(targetContainer,domEventName,eventSystemFlags,listenerPriority,);
createEventListenerWrapperWithPriority函数的名字已经把它做的事情交代得八九不离十了。它会首先根据事件的名称去找对应的事件优先级,然后依据优先级返回不同的事件监听函数。
exportfunctioncreateEventListenerWrapperWithPriority(targetContainer:EventTarget,domEventName:DOMEventName,eventSystemFlags:EventSystemFlags,priority?:EventPriority,):Function{consteventPriority=priority===undefined?getEventPriorityForPluginSystem(domEventName):priority;letlistenerWrapper;switch(eventPriority){caseDiscreteEvent:listenerWrapper=dispatchDiscreteEvent;break;caseUserBlockingEvent:listenerWrapper=dispatchUserBlockingUpdate;break;caseContinuousEvent:default:listenerWrapper=dispatchEvent;break;}returnlistenerWrapper.bind(null,domEventName,eventSystemFlags,targetContainer,);}
最终绑定到root上的事件监听其实是dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchEvent这三个中的一个。它们做的事情都是一样的,以各自的事件优先级去执行真正的事件处理函数。
比如:dispatchDiscreteEvent和dispatchUserBlockingUpdate最终都会以UserBlockingEvent的事件级别去执行事件处理函数。
以某种优先级去执行事件处理函数其实要借助Scheduler中提供的runWithPriority函数来实现:
functiondispatchUserBlockingUpdate(domEventName,eventSystemFlags,container,nativeEvent,){...runWithPriority(UserBlockingPriority,dispatchEvent.bind(null,domEventName,eventSystemFlags,container,nativeEvent,),);...}
这么做可以将事件优先级记录到Scheduler中,相当于告诉Scheduler:你帮我记录一下当前事件派发的优先级,等React那边创建更新对象(即update)计算更新优先级时直接从你这拿就好了。
functionunstable_runWithPriority(priorityLevel,eventHandler){switch(priorityLevel){caseImmediatePriority:caseUserBlockingPriority:caseNormalPriority:caseLowPriority:caseIdlePriority:break;default:priorityLevel=NormalPriority;}varpreviousPriorityLevel=currentPriorityLevel;//记录优先级到Scheduler内部的变量里currentPriorityLevel=priorityLevel;try{returneventHandler();}finally{currentPriorityLevel=previousPriorityLevel;}}更新优先级
以setState为例,事件的执行会导致setState执行,而setState本质上是调用enqueueSetState,生成一个update对象,这时候会计算它的更新优先级,即update.lane:
constclassComponentUpdater={enqueueSetState(inst,payload,callback){...//依据事件优先级创建update的优先级constlane=requestUpdateLane(fiber,suspenseConfig);constupdate=createUpdate(eventTime,lane,suspenseConfig);update.payload=payload;enqueueUpdate(fiber,update);//开始调度scheduleUpdateOnFiber(fiber,lane,eventTime);...},};
重点