大家好,我卡颂。
相信很多关注React的朋友都知道Concurrent Mode,他是React渐进升级策略的产物。
即然是策略,那就有可能调整,这不,根据What happened to concurrent mode?[1],在v18中将不会有Concurrent Mode了。
本文让我们一起了解React渐进升级策略的演进过程。
React新老架构的区别
可以认为采用Fiber架构前的React只支持一种优先级:同步优先级,即所有更新是“同步、不可中断”的形式。
采用Fiber架构后的React支持包括同步优先级在内的多种优先级,或者说新架构支持并发更新。
所以新架构可以模拟老架构的运行机制(即不开启并发更新)。
当新架构开启并发更新后,就能使用React为了解决CPU的瓶颈、IO的瓶颈而设计的并发特性(Concurrent Feature),比如:
- useDeferredValue
- useTransition
- 全功能的Suspense
React所有版本可能的情况
当前所有React版本一定属于如下情况之一:
- v15及之前的老架构
- v16之后的新架构,未开启并发更新,与情况1行为一致
- v16之后的新架构,未开启并发更新,但是启用了一些新功能(比如Automatic Batching)
- v16之后的新架构,开启并发更新
v16、v17默认属于情况2。
之所以划分多种情况,是因为情况4的React一些行为异于情况1、2、3(比如部分以componentWill开头的生命周期函数的调用时机发生变化),也就是说开启并发更新可能造成老代码不兼容。
为了让广大开发者能够平滑过渡,React团队采用了「渐进升级」方案。
早期的渐进升级策略
v16.3新增了StrictMode,对开发者编写的「不符合并发更新规范的代码」作出提示,逐步引导开发者写出规范代码。
此时React团队的「渐进升级」策略是:让页面中可以同时存在三种模式的应用,具体如下:
- Legacy模式,通过ReactDOM.render(
, rootNode)创建的应用遵循该模式。默认关闭StrictMode,表现同情况2 - Blocking模式,通过ReactDOM.createBlockingRoot(rootNode).render(
)创建的应用遵循该模式,作为从Legacy向Concurrent过渡的中间模式,默认开启StrictMode,表现同情况3 - Concurrent模式,通过ReactDOM.createRoot(rootNode).render(
)创建的应用遵循该模式,默认开启StrictMode,表现同情况4
三种模式可用特性对比
在v17之前,大多数事件会冒泡到HTML元素,为了让同一个页面下不同应用独立,v17之后事件会冒泡到应用所在根元素。
所以v17也被称为「垫脚石」版本,他是为「开启并发更新」做铺垫的版本。
当前的渐进升级策略
但是,根据社区的反馈,React团队意识到当前的「渐进升级」策略存在问题。
这三种模式影响的是整个应用,从Legacy升级到Blocking后,由于开启StrictMode,整个应用的「并发不兼容警告」都会上报。从这个角度看,并没有起到「渐进升级」的目的。
另一方面,开发者从新架构中获益更多是由于使用了并发特性,即然新架构支持「开关并发更新」,完全可以在使用了并发特性后再开启并发更新。这样就只需在「使用了并发特性的组件」部分启用StrictMode。
相比划分三种模式,这种以「是否使用并发特性」作为是否开启并发更新的依据,更符合「渐进升级」中「渐进」的理念。
所以在v18中,不再有三种模式,统一使用ReactDOM.createRoot(rootNode).render(
总结
一句话总结:v18以后只会有并发特性,不会有并发模式。
作为一个发展了8年之久的前端框架,要想推进社区完成带有breaking change的迭代升级,注定是困难的。
我们也看到了React团队在此确实付出很多心血。
想问问各位,还学得动么?
参考资料
[1]What happened to concurrent mode?:
https://github.com/reactwg/react-18/discussions/64