前言,在一次react调度更新中,getNextLanes(root, wipLanes)方法会计算出本次更新时的lanes,而这个方法又因为里边充满了很多概念以及大量计算,可能会是我们学习源码的一大障碍,本文主要用来探讨这个方法的流程和详细实现步骤。
关于react17中部分二进制计算的扫盲
getNextLanes流程图
stateDiagram-v2
getNextLanes --> 获得根节点的expiredLanes【1】,suspendedLanes【2】,pingedLanes【3】
获得根节点的expiredLanes【1】,suspendedLanes【2】,pingedLanes【3】 --> expiredLanes!==NoLanes(此表达式成立表示存在expiredLanes)
expiredLanes!==NoLanes(此表达式成立表示存在expiredLanes)--> true
expiredLanes!==NoLanes(此表达式成立表示存在expiredLanes)--> false
true --> nextLanes=expiredLanes
false --> 取得pendingLanes与NonIdleLanes(111111111111111111111111111)的<br>按位与结果,并保存为变量nonIdlePendingLanes
nextLanes=expiredLanes --> nextLanes不等于0
取得pendingLanes与NonIdleLanes(111111111111111111111111111)的<br>按位与结果,并保存为变量nonIdlePendingLanes --> nonIdlePendingLanes为0
取得pendingLanes与NonIdleLanes(111111111111111111111111111)的<br>按位与结果,并保存为变量nonIdlePendingLanes --> nonIdlePendingLanes不为0
nonIdlePendingLanes不为0 --> 计算nonIdlePendingLanes按位与~suspendedLanes的结果并保存为变量nonIdleUnblockedLanes
计算nonIdlePendingLanes按位与~suspendedLanes的结果并保存为变量nonIdleUnblockedLanes --> nonIdleUnblockedLanes为0
计算nonIdlePendingLanes按位与~suspendedLanes的结果并保存为变量nonIdleUnblockedLanes --> nonIdleUnblockedLanes不为0
nonIdleUnblockedLanes不为0 --> 调用getHighestPriorityLanes【4】<br/>(nonIdleUnblockedLanes)<br/>赋值给nextLanes
nonIdleUnblockedLanes为0 --> 计算nonIdlePendingLanes按位与pingedLanes<br>并保存为变量nonIdlePingedLanes
计算nonIdlePendingLanes按位与pingedLanes<br>并保存为变量nonIdlePingedLanes --> nonIdlePingedLanes不为0<br>则调用getHighestPriorityLanes<br>(nonIdlePingedLanes)<br>并赋值给nextlanes
nonIdlePendingLanes为0 --> 计算pendingLanes按位与~suspendedLanes的结果并保存为变量unblockedLanes
计算pendingLanes按位与~suspendedLanes的结果并保存为变量unblockedLanes --> unblockedLanes不为0<br>则调用getHighestPriorityLanes<br>(unblockedLanes)<br>取结果赋值给nextLanes
计算pendingLanes按位与~suspendedLanes的结果并保存为变量unblockedLanes --> unblockedLanes为0<br>则调用getHighestPriorityLanes<br>(pingedLanes)<br>取结果赋值给nextLanes
调用getHighestPriorityLanes【4】<br/>(nonIdleUnblockedLanes)<br/>赋值给nextLanes --> nextLanes不等于0
nonIdlePingedLanes不为0<br>则调用getHighestPriorityLanes<br>(nonIdlePingedLanes)<br>并赋值给nextlanes --> nextLanes不等于0
unblockedLanes不为0<br>则调用getHighestPriorityLanes<br>(unblockedLanes)<br>取结果赋值给nextLanes --> nextLanes不等于0
unblockedLanes为0<br>则调用getHighestPriorityLanes<br>(pingedLanes)<br>取结果赋值给nextLanes --> nextLanes不等于0
nextLanes不等于0 --> nextLanes=pendingLanes按位与getEqualOrHigherPriorityLanes(nextLanes)
nextLanes=pendingLanes按位与getEqualOrHigherPriorityLanes(nextLanes) --> wipLanes!==NoLanes&&wipLanes!==nextLanes&&(wipLanes&suspendedLanes)===NoLanes【5】
wipLanes!==NoLanes&&wipLanes!==nextLanes&&(wipLanes&suspendedLanes)===NoLanes【5】--> 结果为true
wipLanes!==NoLanes&&wipLanes!==nextLanes&&(wipLanes&suspendedLanes)===NoLanes【5】--> 结果为false
结果为true --> nextLanePriority<=wipLanePriority【6】
nextLanePriority<=wipLanePriority【6】--> 表达式结果成立
nextLanePriority<=wipLanePriority【6】--> 表达式结果不成立
表达式结果成立 --> 函数终止返回wipLanes
表达式结果不成立 --> 获得根节点的entangledLanes【7】
获得根节点的entangledLanes【7】 --> entangledLanes不为0
获得根节点的entangledLanes【7】 --> entangledLanes为0
entangledLanes不为0 --> 获得根节点entanglements<br>获得nextLanes按位<br>与entangledLanes的值并保存为lanes
获得根节点entanglements<br>获得nextLanes按位<br>与entangledLanes的值并保存为lanes --> 遍历lanes<br>并更新nextLanes【8】
遍历lanes<br>并更新nextLanes【8】 --> 函数终止返回nextLanes
entangledLanes为0 --> 函数终止返回nextLanes
mermaid移动端支持不佳,所以加一个图片版
详细概念解释
- expiredLanes . 一般保存的是已经过期的的任务即expirationTime<=currentTime的任务
- suspendedLanes. 字面意思理解即被挂起的任务。实际作用也差不多,在
SuspenseComponent
类型的fiber节点的更新流程中的completework
阶段,在一定条件下会将该任务挂起,并将该任务的lane保存到root节点的suspendedLanes
上。另外就是scheduleUpdateOnFiber
中在即将开始更新前重置根节点的该字段,具体的行为是suspendedLanes
上只保留比当前更新的优先级更高的任务,代码语言为
var higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111
root.suspendedLanes &= higherPriorityLanes;
- pingedLanes. 在Concurrent更新模式下,若
renderRootConcurrent
工作时,SuspenseComponent
类型的fiber节点在completework
阶段会在一定条件下将workInProgressRootExitStatus
标记为RootSuspended
,则最终在finishConcurrentRender
时,将root
节点的suspendedLanes
保存到pingedLanes
上,详细来说是保存了这两个字段的“交集”:
root.pingedLanes |= root.susp
endedLanes & pingedLanes;
getHighestPriorityLanes
. 从当前lanes是取得优先级最高的lane。wipLanes
是由该函数形参传入的。nextLanePriority
. 上边流程中每一次的nextlanes
赋值都会同时给该变量赋予不同的值。wipLanePriority
则在getHighestPriorityLanes
每次调用的时候赋值。entangledLanes
. 直接贴官方注释啦:
A lane is said to be entangled with another when it's not allowed to render in a batch that does not also include the other lane. Typically we do this when multiple updates have the same source, and we only want to respond to the most recent event from that source.
- 遍历过程
while (lanes > 0) {
var index = pickArbitraryLaneIndex(lanes); // 返回一个二进制数中左起第一个位置为1的数的从右向左数的第n位 比方0b 101 就是2;
var lane = 1 << index; // 这个lane的二进制形式表示,比方 index为2 1左移两位就是 100
nextLanes |= entanglements[index]; //更新 nextlanes
lanes &= ~lane; // lanes中消除已经遍历过得lane
}
总结: 需要说明的是,以上判断某个值是否为0时,总是会判断当前值是否不为0,而后才会去判断为0的逻辑,因而从以上流程图中不难看出,在计算下一次lanes时react找寻本次更新的lanes时首先会从
expiredLanes
上取值,若该值不存在,则会去pendingLanes
上取值,而在pendingLanes
上取得结果时,会将suspendedLanes
消去,最后才会是去pingedLanes
上找寻结果。最后处理完entangledLanes
后将结果返回。