前言
在React的发展历程中,有几个重要的版本被广泛关注,它们代表了React框架在不同阶段的主要特性和改进。下面是一些关键的React版本及其主要区别
各版本区别
React 16以前
使用的调度方式是:Stack Reconciler 【协调器】
这个策略像函数调用栈一样,会深度优先遍历所有的 Virtual DOM 节点,进行Diff。它一定要等整棵 Virtual DOM 计算完成之后,才将任务出栈释放主线程。所以,在浏览器主线程被 React 更新状态任务占据的时候,用户与浏览器进行任何的交互都不能得到反馈,只有等到任务结束,才能突然得到浏览器的响应。【同步更新,不可中断】
React 这样的调度策略对动画的支持也不好。如果 React 更新一次状态,占用浏览器主线程的时间超过 16.6 ms,就会被人眼发现前后两帧不连续,给用户呈现出动画卡顿的效果。【主流的浏览器刷新频率为60Hz,即每(1000ms / 60Hz)16.6ms浏览器刷新一次。我们知道,JS可以操作DOM,GUI渲染线程与JS线程是互斥的。所以JS脚本执行和浏览器布局、绘制不能同时执行。】
Reconciler采用递归的方式创建虚拟DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,造成卡顿。
React 16
React16 对底层架构进行了重构,重构后的架构分为三个部分 Scheduler(调度器) 、 Reconciler(协调器) 和 Renderer(渲染器)。
调度器主要负责优先级调度,高优先级的任务优先进入 Reconciler 。
协调器主要负责根据自变量变化计算出UI变化。
渲染器主要负责把UI变化渲染到宿主环境中。
并引入 fiber架构 作为VDOM的实现,也是新架构的基石。
引入了以下关键特性:
并发渲染:为React添加了并发渲染的能力,可以提高性能。
Hooks:允许在不编写类的情况下使用state和其他React特性。v16.8
错误边界:允许在组件树中捕获JavaScript错误,并记录它们,同时显示一个备用的UI。
Fiber架构:这是一个新的渲染引擎,用于提高渲染性能和更平滑的用户体验。
那新架构是怎么解决前面提到的问题的呢?
问题一:新架构引入 fiber ,借鉴操作系统时间分片 的概念,把 Reconciler 的更新流程从递归不可中断,改为了用 while 循环遍历链表,并通过 shouldYeild 方法判断是否中断循环,让浏览器优先渲染页面。
问题三:新架构 VDOM 树(fiber树)采用树状链表的结构,用全局变量记录节点的引用,通过记录的节点即可找到其他的节点。
问题二:采用双缓存机制------------ React 运行时,最多存在两颗 VDOM 树,也就是 fiber 树,一棵 fiber 树对应页面上的 UI(前缓冲区),另一棵 fiber 树代表正要更新到屏幕上的UI(后缓冲区)。这样就可以等当新的页面的状态全部计算完毕后再一次性更新到页面上。
React 17
较小的更新,重点在于使React和React Native的应用程序之间的组件协作变得更加容易。它引入了新的Context API和startTransition API。
新的 JSX 转换逻辑,编写 JSX 代码将不再需要手动导入 React 包,编译器会针对 JSX 代码进行自动导入(React/jsx-runtime)和优化
React 18
并发模式:默认启用并发模式,它允许React在渲染过程中暂停和恢复,以便处理高优先级任务,如用户输入或动画。
自动批处理:优化了状态更新的批处理,以提高性能。
在某些场景下 我们可能不需要批处理状态更新, 此时我们需要用到 react-dom 提供的flushSync函数, 该函数需传入一个回调, 并且会同步刷新回调中的状态更新
import { useState } from 'react'
import { flushSync } from 'react-dom'
function App() {
const [num1, setNum1] = useState(1)
const [num2, setNum2] = useState(1)
const add = () => {
setTimeout(() => {
flushSync(() => {
setNum1((pre) => pre + 1)
})
flushSync(() => {
setNum2((pre) => pre + 1)
})
})
}
console.log('渲染了')
console.log(num1, num2)
return (
<div className="App">
<header className="App-header">react 18</header>
<p>num1 : {num1}</p>
<p>num2 : {num2}</p>
<button onClick={add}>+1</button>
</div>
)
}
export default App
新的Suspense和lazy API:简化了代码分割和懒加载组件的过程。
服务器端渲染改进:改进了服务器端渲染的性能和可靠性。