react源码解读 getNextLanes

1,629 阅读1分钟

前言,在一次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移动端支持不佳,所以加一个图片版

getnexlanes.png 详细概念解释

  1. expiredLanes . 一般保存的是已经过期的的任务即expirationTime<=currentTime的任务
  2. suspendedLanes. 字面意思理解即被挂起的任务。实际作用也差不多,在SuspenseComponent类型的fiber节点的更新流程中的completework阶段,在一定条件下会将该任务挂起,并将该任务的lane保存到root节点的suspendedLanes上。另外就是scheduleUpdateOnFiber中在即将开始更新前重置根节点的该字段,具体的行为是suspendedLanes上只保留比当前更新的优先级更高的任务,代码语言为
    var higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111
    root.suspendedLanes &= higherPriorityLanes;
  1. pingedLanes. 在Concurrent更新模式下,若renderRootConcurrent工作时,SuspenseComponent类型的fiber节点在completework阶段会在一定条件下将workInProgressRootExitStatus标记为RootSuspended,则最终在finishConcurrentRender时,将root节点的suspendedLanes保存到pingedLanes上,详细来说是保存了这两个字段的“交集”:
root.pingedLanes |= root.susp
endedLanes & pingedLanes;
  1. getHighestPriorityLanes. 从当前lanes是取得优先级最高的lane。
  2. wipLanes是由该函数形参传入的。
  3. nextLanePriority . 上边流程中每一次的nextlanes赋值都会同时给该变量赋予不同的值。wipLanePriority则在getHighestPriorityLanes每次调用的时候赋值。
  4. 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.

  1. 遍历过程
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后将结果返回。