React使用concurrent模式,产生更新需要时(如setState),执行markUpdateLaneFromFiberToRoot函数,执行
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
为fiber.lanes赋值,表示该组件有更新,在render阶段执行beginwork时,其中需要拿fiber.lanes和此次任务更新的优先级做比较,判断该组件的优先级够不够,
else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
...
}
但是之后会开始生成组件子节点结点(类组件render函数返回的jsx或者函数组件返回值)前会执行
workInProgress.lanes = NoLanes;
如果有一个任务需要渲染100个组件,执行时间很长,当beginwork到第50个组件fiber时时间片用完且有一个高优先级任务产生对其进行插队,之前的任务被取消,但是之前执行完beginwork的前50个组件fiber的lanes被清空了,如果这个高优先级任务执行完,再重新执行100个组件fiber的渲染,前50个fiber的lanes不是就找不到了
问题分析
总体上来说,React会保存两个版本的lanes,分别存储在workInProgress fiber树中,和current fiber树中
一次render总是对应着一颗workInProgress树的构建,存储在它里面的lanes就是这个时候计算出来的,原因也很简单,当每一轮render进行时,渲染它所对应的renderLane是可以确定的,所以我们我可知道这轮render会结束那些任务(每一个任务都是和一个lane绑定的),那么刨除掉这些任务对应的lanes,剩下的lanes就是上面workInProgress fiber树中定义的那些lanes。
从上面的分析中你应该已经知道问题的答案了,既然workInProgress树中存储的lanes要下一次render才用得到,也就意味着,在当前render还没结束之前我们可以抛弃掉这些lanes而不会造成什么影响,大不了待会我在重新构建workInProgress树时再算一次就行
实际例子
考虑下面的伪代码,这个组件会在mount后会调用
fetchData
获取远程数据,获取数据后调用setList
设置list
,同时还有一个输入框,当输入框内容变更时会调用setName
设置name
存在着这么一种情况,当远程数据返回后,并且返回数据量很大渲染可能会很耗时,此时当
List
组件渲染到一半时,用户用在输入框中输入值,此时React
,就会中止List
的渲染而去渲染用户的输入值name
(这个也很好理解对于接口的返回值对页面的影响,让用户多等个几十ms甚至更长的时间这是完全可以接受的,但是要是在输入框输入一个值,界面要等个几十ms才会发生变更,用户反而会对这种情况更敏感,第一印象就是你的应用很卡,不跟手),接下来我们就来分析一下这段时间内React
做了那些事。Mount后App组件的状态:
接口返回后App组件的状态
React调度了updateQueue中优先级最高的更新,开始一轮render过程,此时由于更新列表中只有一个更新
renderLane
就是setList
发起的任务的任务的lane
,也就是0b0010
用户在输入框中输入值,中止了第三步的更新上面workInProgress中计算到一半的lanes和states会被丢弃
调度并渲染用户输入事件所产生的那个的那个更新任务,由于接口造成的那个任务优先级较低会被暂时搁置
用户所产生的那个任务完成,workInProgress中的lanes,和states会成为current states
渲染剩余的任务,也就是接口所产生的那个任务
接口数据也渲染完成,workInProgress中的lanes,和states会成为current states
源码出处,如果你对源码已经有了个大体的概念,下面的关键代码处,会让你更好的理解上面的解释