为什么需要优先级
优先级机制最终目的是为了实现高优先级任务优先执行,低优先级任务延后执行。
实现这一目的的本质就是在低优先级任务执行时,有更高优先级任务进来的话,可以打断低优先级任务的执行。
同步模式下的react运行时
我们知道在同步模式下,从 setState
到 虚拟DOM遍历,再到真实DOM更新,整个过程都是同步执行且无法被中断的,这样可能就会出现一个问题 —— 用户事件触发的更新被阻塞。
什么是用户事件触发的更新被阻塞?如果 React
正在进行更新任务,此时用户触发了交互事件,且在事件回调中执行了 setState
,在同步模式下,这个更新任务需要 等待
当前正在更新的任务完成之后,才会被执行。假如当前 React
正在进行的更新任务耗时比较久,用户事件触发的更新任务不能及时被执行,造成下个更新任务被阻塞,从而形成了卡顿。
这时候,我们就希望能够及时响应用户触发的事件,优先执行用户事件触发的更新任务,也就是我们说的异步模式
我们可以比较一下,同步模式下和异步模式(优先级机制
)下更新任务执行的差异
import React from "react";
import "./styles.css";
export default class extends React.Component {
constructor() {
super();
this.state = {
list: new Array(10000).fill(1),
};
this.domRef = null;
}
componentDidMount() {
setTimeout(() => {
console.log("setTimeout 准备更新", performance.now());
this.setState(
{
list: new Array(10000).fill(Math.random() * 10000),
updateLanes: 16
},
() => {
console.log("setTimeout 更新完毕", performance.now());
}
);
}, 100);
setTimeout(() => {
this.domRef.click();
}, 150);
}
render() {
const { list } = this.state;
return (
<div
ref={(v) => (this.domRef = v)} className="App" onClick={() => { console.log("click 准备更新", performance.now()); this.setState( { list: new Array(10000).fill(2), updateLanes: 1 }, () => { console.log("click 更新完毕", performance.now()); } ); }} > {list.map((i, index) => ( <h1 key={i + +index}>Hello {i}</h1>
))} </div>
);
}
}
click事件
触发的更新,会比 setTimeout
触发的更新更优先执行,做到了及时响应用户事件,打断 setTimeout
更新任务(低优先级任务
)的执行。
如何运用优先级机制优化react运行时
为了解决同步模式渲染下的缺陷,我们希望能够对 react
做出下面这些优化
- 确定不同场景下所触发更新的优先级,以便我们可以决定优先执行哪些任务
- 若有更高优先级的任务进来,我们需要打断当前进行的任务,然后执行这个高优先级任务
- 确保低优先级任务不会被一直打断,在一定时间后能够被升级为最高优先级的任务
确定不同场景下的调度优先级
看过 react
源码的小伙伴可能都会有一个疑惑,为什么源码里面有那么多优先级相关的单词??怎么区分他们呢?
其实在 react
中主要分为两类优先级,scheduler
优先级和 lane
优先级,lane
优先级下面又派生出 event
优先级
- lane 优先级:主要用于任务调度前,对当前正在进行的任务和被调度任务做一个优先级校验,判断是否需要打断当前正在进行的任务
- event 优先级:本质上也是lane优先级,lane优先级是通用的,event优先级更多是结合浏览器原生事件,对lane优先级做了分类和映射
- scheduler 优先级:主要用在时间分片中任务过期时间的计算
lane优先级
可以用赛道的概念去理解lane优先级,lane优先级有31个,我们可以用31位的二进制值去表示,值的每一位代表一条赛道对应一个lane优先级,赛道位置越靠前,优先级越高
优先级 | 十进制值 | 二进制值 | 赛道位置 |
---|---|---|---|
NoLane | 0 | 0000000000000000000000000000000 | 0 |
SyncLane | 1 | 0000000000000000000000000000001 | 0 |
InputContinuousHydrationLane | 2 | 0000000000000000000000000000010 | 1 |
InputContinuousLane | 4 | 0000000000000000000000000000100 | 2 |
DefaultHydrationLane | 8 | 0000000000000000000000000001000 | 3 |
DefaultLane | 16 | 0000000000000000000000000010000 | 4 |
TransitionHydrationLane | 32 | 0000000000000000000000000100000 | 5 |
TransitionLane1 | 64 | 0000000000000000000000001000000 | 6 |
TransitionLane2 | 128 | 0000000000000000000000010000000 | 7 |
TransitionLane3 | 256 | 0000000000000000000000100000000 | 8 |
TransitionLane4 | 512 | 0000000000000000000001000000000 | 9 |
TransitionLane5 | 1024 | 0000000000000000000010000000000 | 10 |
TransitionLane | 2048 | 0000000000000000000100000000000 | 11 |
TransitionLane7 | 4096 | 0000000000000000001000000000000 | 12 |
TransitionLane8 | 8192 | 0000000000000000010000000000000 | 13 |
TransitionLane9 | 16384 | 0000000000000000100000000000000 | 14 |
TransitionLane10 | 32768 | 0000000000000001000000000000000 | 15 |
TransitionLane11 | 65536 | 0000000000000010000000000000000 | 16 |
TransitionLane12 | 131072 | 0000000000000100000000000000000 | 17 |
TransitionLane13 | 262144 | 0000000000001000000000000000000 | 18 |
TransitionLane14 | 524288 | 0000000000010000000000000000000 | 19 |
TransitionLane15 | 1048576 | 0000000000100000000000000000000 | 20 |
TransitionLane16 | 2097152 | 0000000001000000000000000000000 | 21 |
RetryLane1 | 4194304 | 0000000010000000000000000000000 | 22 |
RetryLane2 | 8388608 | 0000000100000000000000000000000 | 23 |
RetryLane3 | 16777216 | 0000001000000000000000000000000 | 24 |
RetryLane4 | 33554432 | 0000010000000000000000000000000 | 25 |
RetryLane5 | 67108864 | 0000100000000000000000000000000 | 26 |
SelectiveHydrationLane | 134217728 | 0001000000000000000000000000000 | 27 |
IdleHydrationLane | 268435456 | 0010000000000000000000000000000 | 28 |
IdleLane | 536870912 | 0100000000000000000000000000000 | 29 |
OffscreenLane | 1073741824 | 1000000000000000000000000000000 | 30 |
event优先级
EventPriority | Lane | 数值 | |
---|---|---|---|
DiscreteEventPriority | 离散事件。click、keydown、focusin等,事件的触发不是连续,可以做到快速响应 | SyncLane | 1 |
ContinuousEventPriority | 连续事件。drag、scroll、mouseover等,事件的是连续触发的,快速响应可能会阻塞渲染,优先级较离散事件低 | InputContinuousLane | 4 |
DefaultEventPriority | 默认的事件优先级 | DefaultLane | 16 |
IdleEventPriority | 空闲的优先级 | IdleLane | 536870912 |
scheduler优先级
SchedulerPriority | EventPriority | 大于>17.0.2 | 小于>17.0.2 |
---|---|---|---|
ImmediatePriority | DiscreteEventPriority | 1 | 99 |
UserblockingPriority | Userblocking | 2 | 98 |
NormalPriority | Default |