Blogger Information
Blog 13
fans 0
comment 0
visits 10298
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
小推理:React18比老版React更优秀的一个地方
P粉962186587
Original
586 people have browsed it

React18已经进入RC(release candidate)阶段,距离正式版只有一步之遥了。

v18新增了很多特性,今天,我们不聊新特性,而是来讲讲v18相比老版更优秀的一个细节:

v18中,组件render的次数可能更少
欢迎加入人类高质量前端框架群,带飞

状态从何而来
在如下组件中:

  1. function App() {
  2. const [num, update] = useState(0);
  3. // ...省略
  4. }

App组件render后会执行useState,返回num的最新值。

也就是说,组件必须render,才能知道最新的状态。为什么会这样呢?

考虑如下触发更新的代码:

  1. const [num, update] = useState(0);
  2. const onClick = () => {
  3. update(100);
  4. update(num => num + 1);
  5. update(num => num * 3);
  6. }

onClick执行后触发更新,更新导致App组件render,进而useState执行。

在useState内部,会遵循如下流程计算num:

update(100)将num变为100
update(num => num + 1)将num变为100 + 1 = 101
update(num => num 3)将num变为101 3 = 303
即,App组件render时,num为303。

所以,状态的计算需要先收集触发的更新,再在useState中统一计算。

对于上述例子,将更新分别命名为u0~u2,则状态的计算公式为:

  1. baseState -> u0 -> u1 -> u2 = newState

Concurrent带来的变化
Concurrent(并发)为React带来了优先级的概念,反映到状态计算上,根据触发更新的场景,更新拥有不同优先级(比如onClick回调中触发的更新优先级高于useEffect回调中触发的更新)。

表现在计算状态中的区别就是,如果某个更新优先级低,则会被跳过。

假设上述例子中u1优先级低,那么App组件render时,计算num状态的公式为:

  1. // 其中u1因为优先级低,被跳过
  2. baseState -> u0 -> u2 = newState

即:

update(100)将num变为100
update(num => num 3)将num变为100 3 = 300
显然这个结果是不对的。

所以,并发情况下React计算状态的逻辑会更复杂。具体来讲,可能包含多轮计算。

当计算状态时,如果某次更新被跳过,则下次计算时会从被跳过的更新继续往后计算。

比如上例中,u1被跳过。当u1被跳过时,num为100。此时的状态100,以及u1和他后面的所有更新都会保存下来,参与下次计算。

在例子中即为u1、u2保存下来。

下次更新的情况如下:

初始状态为100,update(num => num + 1)将num变为100 + 1 = 101
update(num => num 3)将num变为101 3 = 303
可见,最终的结果303与同步的React是一致的,只是需要render两次。

同步的React render一次,结果为303。

并发的React render两次,结果分别为300(中间状态),303(最终状态)。

新旧Concurrent的区别
从上例我们发现,组件render的次数受有多少更新被跳过影响,实际可能不止render两次,而是多次。

在老版并发的React中,表示优先级的是一个被称为expirationTime的时间戳。比较更新是否应该被跳过的算法如下:

  1. // 更新优先级是否小于render的优先级
  2. if (updateExpirationTime < renderExpirationTime) {
  3. // ...被跳过
  4. } else {
  5. // ...不跳过
  6. }

在这种逻辑下,只要优先级低,就会被跳过,就意味着多一次render。

在新版并发的React中,优先级被保存在31位的二进制数中。

举个例子:

  1. const renderLanes = 0b0101;
  2. u1.lane = 0b0001;
  3. u2.lane = 0b0010;

其中renderLanes是本次更新指定的优先级。

比较优先级的函数为:

  1. function isSubsetOfLanes(set, subset) {
  2. return (set & subset) === subset;
  3. }

其中:

  1. // true
  2. isSubsetOfLanes(renderLanes, u1.lane)
  3. // false
  4. isSubsetOfLanes(renderLanes, u2.lane)

u1.lane包含于renderLanes中,代表这个更新拥有足够优先级。

u2.lane不包含于renderLanes中,代表这个更新没有足够优先级,被跳过。

但是被跳过的更新(例子中的u2)的lane会被重置为0,即:

  1. u2.lane = 0b0000;

显然任何lanes都是包含0的:

  1. // true
  2. isSubsetOfLanes(renderLanes, 0)

所以这个更新一定会在下次处理。换言之,在新版并发的React中,由于优先级原因被跳过,导致的重复render,最多只会有2次。

总结
相比于老版并发的React,新版并发的React在render次数上会更有优势。

反映到用户的感官上,用户会更少看到未计算完全的中间状态。

以上就是全部内容,希望对大家有所帮助,

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post