react16 的新架构
2023-04-04 14:07:48
相较于 React15,React16 中新增了 Scheduler(调度器)
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入 Reconciler
- Reconciler(协调器)—— 负责找出变化的组件
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
Scheduler(调度器)
Scheduler 是独立于 React 的库
浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们,部分浏览器已经实现了这个 API,就是 requestIdleCallback api,但是由于兼容性、触发频率不稳定等因素,React 放弃使用
React 实现了功能更完备的 requestIdleCallback polyfill,这就是 Scheduler。除了在空闲时触发回调的功能外,Scheduler 还提供了多种调度优先级供任务设置。
React 内部一共有五种优先级
var timeout
switch (priorityLevel) {
case ImmediatePriority:
timeout = IMMEDIATE_PRIORITY_TIMEOUT
break
case UserBlockingPriority:
timeout = USER_BLOCKING_PRIORITY_TIMEOUT
break
case IdlePriority:
timeout = IDLE_PRIORITY_TIMEOUT
break
case LowPriority:
timeout = LOW_PRIORITY_TIMEOUT
break
case NormalPriority:
default:
timeout = NORMAL_PRIORITY_TIMEOUT
break
}
var expirationTime = startTime + timeout
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
不用优先级对应着不同的过期时间,我们在一个大型的 react 项目中的某一刻,一定会存在很多任务,这些任务的优先级不同,我们可以将这些任务分为
- 已过期任务
- 未过期任务
同样的在 schedule 内部维护了两个队列
- timerQueue 保存未过期任务
- taskQueue 保存已过期任务
当有新的未过期任务被注册,我们将其插入到 timerQueue,并重新排列 timerQueue 中任务的顺序
当 timerQueue 中有任务过期时,我们将其取出加入到 taskQueue,取出 taskQueue 中最早过期的任务并执行
为了能在O(1)复杂度找到两个队列中事件最早的那个任务,scheduler 使用小顶堆实现了优先队列
堆排序(大顶堆、小顶堆)----C语言 - Luv3 - 博客园
Fiber
React15 及以前,Reconciler 采用递归的方式创建虚拟 DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,造成卡顿。
为了解决这个问题,React16 将递归的无法中断的更新重构为异步的可中断更新,由于曾经用于递归的虚拟 DOM 数据结构已经无法满足需要。于是,全新的 Fiber 架构应运而生
每个 Fiber 节点对应一个 React element,保存了该组件的类型(函数组件/类组件/原生组件...)、对应的 DOM 节点等信息,保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新...)。
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode
) {
// 作为静态数据结构的属性
this.tag = tag
this.key = key
this.elementType = null
this.type = null
this.stateNode = null
// 用于连接其他Fiber节点形成Fiber树
this.return = null
this.child = null
this.sibling = null
this.index = 0
this.ref = null
// 作为动态的工作单元的属性
this.pendingProps = pendingProps
this.memoizedProps = null
this.updateQueue = null
this.memoizedState = null
this.dependencies = null
this.mode = mode
this.effectTag = NoEffect
this.nextEffect = null
this.firstEffect = null
this.lastEffect = null
// 调度优先级相关
this.lanes = NoLanes
this.childLanes = NoLanes
// 指向该fiber在另一次更新时对应的fiber
this.alternate = null
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
React 使用“双缓存”来完成 Fiber 树的构建与替换——对应着 DOM 树的创建与更新
在 React 中最多会同时存在两棵 Fiber 树。当前屏幕上显示内容对应的 Fiber 树称为 current Fiber 树,正在内存中构建的 Fiber 树称为 workInProgress Fiber 树。
current Fiber 树中的 Fiber 节点被称为 current fiber,workInProgress Fiber 树中的 Fiber 节点被称为 workInProgress fiber,他们通过 alternate 属性连接。
currentFiber.alternate === workInProgressFiber
workInProgressFiber.alternate === currentFiber
2
React 应用的根节点通过使 current 指针在不同 Fiber 树的 rootFiber 间切换来完成 current Fiber 树指向的切换。
即当 workInProgress Fiber 树构建完成交给 Renderer 渲染在页面上后,应用根节点的 current 指针指向 workInProgress Fiber 树,此时 workInProgress Fiber 树就变为 current Fiber 树。
每次状态更新都会产生新的 workInProgress Fiber 树,通过 current 与 workInProgress 的替换,完成 DOM 更新。