要实现Concurrent Mode,最关键是实现异步可中断的更新。基于这个前提,React花费2年时间重构完成了Fiber架构,将单个组件作为工作单元,使以组件为粒度的“异步可中断的更新”成为可能。
架构的驱动力 —— Scheduler
如果我们同步运行Fiber架构(通过ReactDOM.render),则Fiber架构与重构前并无区别。但是当我们配合时间切片,就能根据宿主环境性能,为每个工作单元分配一个可运行时间,实现“异步可中断的更新”,于是,scheduler(调度器)产生了。
架构运行策略 —— lane模型
到目前为止,React可以控制更新在Fiber架构中运行/中断/继续运行。基于当前的架构,当一次更新在运行过程中被中断,过段时间再继续运行,这就是“异步可中断的更新”。当一次更新在运行过程中被中断,转而重新开始一次新的更新,我们可以说:后一次更新打断了前一次更新。这就是优先级的概念:后一次更新的优先级更高,他打断了正在进行的前一次更新。
我们需要一个模型控制不同优先级之间的关系与行为,比如控制多个优先级之间如何互相打断的逻辑、控制优先级如何升降、控制如何赋予本次更新优先级。于是lane模型诞生了。
上层实现
从源码层面讲,Concurrent Mode是一套可控的“多优先级更新架构”。
基于该架构之上可以实现以下意思的功能:
batchedUpdates
如果我们在一次事件回调中触发多次更新,他们会被合并为一次更新进行处理。如下代码执行只会触发一次更新。
这种合并多个更新的优化方式被称为batchedUpdates,batchedUpdates在很早的版本就存在了,不过之前的实现局限很多(脱离当前上下文环境的更新不会被合并),在Concurrent Mode中,是以优先级为依据对更新进行合并的,使用范围更广。
onClick() {
this.setState({stateA: 1});
this.setState({stateB: false});
this.setState({stateA: 2});
}
Suspense
Suspense可以在组件请求数据时展示一个pending状态。请求成功后渲染数据。本质上讲Suspense内的组件子树比组件树的其他部分拥有更低的优先级。
useDeferredValue
useDeferredValue返回一个延迟响应的值,该值可能“延后”的最长时间为timeoutMs。
在useDeferredValue内部会调用useState并触发一次更新。这次更新的优先级很低,所以当前如果有正在进行中的更新,不会受useDeferredValue产生的更新影响。所以useDeferredValue能够返回延迟的值。当超过timeoutMs后useDeferredValue产生的更新还没进行(由于优先级太低一直被打断),则会再触发一次高优先级更新。
const deferredValue = useDeferredValue(value, { timeoutMs: 2000 });
参考链接
-
react技术揭秘
-
杨艺韬讲堂
关于作者
作者:Wandra
内容:算法 | 趋势 |源码|Vue | React | CSS | Typescript | Webpack | Vite | GithubAction | GraphQL | Uniqpp。
专栏:欢迎关注🌹
本专栏致力于分析热门项目,如果本文对你有帮助的话,欢迎点赞或关注。