关于javascript:小推理React18比老版React更优秀的一个地方

32次阅读

共计 2131 个字符,预计需要花费 6 分钟才能阅读完成。

大家好,我卡颂。

React18曾经进入 RC(release candidate) 阶段,间隔正式版只有一步之遥了。

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

v18 中,组件 render 的次数可能更少

欢送退出人类高质量前端框架群,带飞

状态从何而来

在如下组件中:

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

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

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

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

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

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

useState 外部,会遵循如下流程计算num

  1. update(100)num 变为100
  2. update(num => num + 1)num 变为100 + 1 = 101
  3. update(num => num * 3)num 变为101 * 3 = 303

即,App组件 render 时,num为 303。

所以,状态的计算须要先收集 触发的更新 ,再在useState 中对立计算。

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

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

Concurrent 带来的变动

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

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

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

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

即:

  1. update(100)num 变为100
  2. update(num => num * 3)num 变为100 * 3 = 300

显然这个后果是不对的。

所以,并发状况下 React 计算状态的逻辑会更简单。具体来讲,可能蕴含多轮计算。

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

比方上例中,u1被跳过。当 u1 被跳过期,num为 100。此时的状态 100,以及 u1他前面的所有更新 都会保留下来,参加下次计算。

在例子中即为 u1u2 保留下来。

下次更新的状况如下:

  1. 初始状态为 100update(num => num + 1)num变为100 + 1 = 101
  2. update(num => num * 3)num 变为101 * 3 = 303

可见,最终的后果 303 与 同步的 React是统一的,只是须要 render 两次。

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

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

新旧 Concurrent 的区别

从上例咱们发现,组件 render 的次数受 有多少更新被跳过 影响,理论可能不止 render 两次,而是屡次。

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

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

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

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

举个例子:

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

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

比拟 优先级 的函数为:

function isSubsetOfLanes(set, subset) {return (set & subset) === subset;
}

其中:

// true
isSubsetOfLanes(renderLanes, u1.lane)

// false
isSubsetOfLanes(renderLanes, u2.lane)

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

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

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

u2.lane = 0b0000;

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

// true
isSubsetOfLanes(renderLanes, 0)

所以这个更新肯定会在下次解决。换言之,在新版 并发的 React中,因为 优先级起因被跳过 ,导致的 反复 render,最多只会有 2 次。

总结

相比于老版 并发的 React,新版 并发的 Reactrender 次数上会更有劣势。

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

正文完
 0