React 的并发特性
第一次知道 React 18 出了并发特性之后,我的第一反应是可以同时更新,那性能是大大的提升啊。我得赶紧把这个和领导提一下,把公司项目的 React 升到最新,性能怕是大大提升 50% 不止哦。就在找领导的路上,我脑瓜子灵光一闪,好像不对啊!js 不是单线程执行的吗?咋回事?冷静下来的我赶紧回去查阅了一下资料:
- 并发
并发是指可以执行多个任务,但是同时只能执行一个。举个例子: 你在掘金浏览文章,然后突然来了个线上问题,你立马就停止浏览文章去修复问题,修完之后又接着浏览文章。这个例子中,你并发了两件事,同一时间只能干一件事,一件事没干完,可以停下来干别的事,完了再接着干上一件事。 - 并行
并行是指可以执行多个任务,这些任务可以同时执行。举个例子,你下班回家之后,一边看电视,一边吃饭,同时就把这两件事干了,这就是并行。
原来并发不是我想的那个“并发”啊,恩,这就和 js 单线程对上了,幸好刚刚没去给领导说!
React 新架构简介
前面介绍了 React 架构分为三个部分 Scheduler(调度器) 、 Reconciler(协调器) 和 Renderer(渲染器) 。
在 React 的工作流程中, Reconciler 工作的阶段被称为 render 阶段, Renderer 工作的阶段被称为 commit 阶段。
Reconciler
结合前面文章的内容,Reconciler 主要的工作是构建 wip Fiber 树,并为有改动的节点打上对应的 flags(节点更新和增删等),工作完成后将构建完成的 wip Fiber 树转交给 Renderer。详细是如何工作后面会出一篇文章。
注意:render阶段是可能被打断的,React 的并发就并发在这个阶段。
Renderer
在这个阶段主要根据 Reconciler 转交过来的 wip Fiber树,将对应改动 commit(提交) 到页面上,通俗点就是在这个阶段操作 DOM 。并在不同的时机执行类组件的生命周期函数,以及函数组件的 Effect。详细是如何工作后面会出一篇文章。 注意 commit 阶段不会中断,一旦开始就会同步执行一直到完成。
Scheduler
调度器每次会选出优先级最高的任务执行,算法采用的是小顶堆。详细实现后续会单独出一篇文章。
React 运行流程
流程
个人理解:React 的运行流程分为 3 步:
- 生成任务:通过初次渲染或者交互产生任务(render + commit 的流程)。
- 调度任务:在已生成的任务中挑出一个最高优先级的任务。
- 执行任务:同步或者并发执行当前任务。
下面是流程图:
我们主要看看执行任务的过程中render的执行情况:
当前任务 A 如果满足并发的条件,会进入并发的render流程开启时间分片(Time Slice),通过 shouldYield() 判断每 5ms 终止一次循环并重新调度一个任务 B ,如果 A 的优先级低于 B ,就停止执行 A ,优先执行 B 任务。 如果 A的优先级和 B 一致或者 A 和 B 是同一个任务,那么继续执行 A。
当前任务 A 不满足并发的条件,则进入同步执行流程,直到 A 执行完成之后再执行下一个任务。
开启并发更新的条件
先说结论:
- 优先级不为 sync
- 不包含阻塞更新
- 不包含已经过期的更新任务
- 当前任务没有过期 以上条件同时满足即可进入并发render流程。
下面直接看看代码的判断
这段代码是初步判断能否并发执行任务,但是具体是否开启并发执行还得进一步的判断,下面看看performConcurrentWorkOnRoot 中具体的判断
并发更新总结
通过 api 开启并发更新的本质就是通过 api 降低当前更新的优先级,使其满足并发更新的条件。具体如何降低后续详解。
最后
感谢大家的阅读,有不对的地方也欢迎大家指出来。
参考
React 18.2.0 源码