理解 React 的Fiber 架构,Diff 算法,Jsx转换等原理1,如何理解 React 的 Fiber 架构 R
1,如何理解 React 的 Fiber 架构
React Fiber 是 React 16 中引入的一种新的架构,旨在解决 React 在大型应用中的性能问题和提高可维护性。Fiber 架构的主要目标是实现增量渲染(Incremental Rendering),即将渲染工作分为多个小任务,并在浏览器的空闲时间段内执行这些任务。这样可以避免长时间的任务阻塞浏览器的主线程,从而提高应用的响应速度。
Fiber 架构的核心概念:
- Fiber:Fiber 是一个轻量级的数据结构,代表一个组件(或原生DOM元素)的工作单元。每个 Fiber 节点都包含组件的状态、组件实例、组件类型等信息。Fiber 构成了一棵树,称为 Fiber 树,它与 React 组件树是一一对应的。
- 双缓冲技术:Fiber 架构采用双缓冲技术,即维护两棵 Fiber 树。一棵是当前屏幕上的树(current Fiber tree),另一棵是正在构建的新树(work-in-progress Fiber tree)。当新的更新发生时,React 会在 work-in-progress Fiber tree 上进行操作,而不是直接修改当前屏幕上的树。一旦 work-in-progress Fiber tree 完成构建,React 会将其切换为当前屏幕上的树。
- 两个阶段:Fiber 架构将渲染过程分为两个阶段 - 协调(Reconciliation)阶段和提交(Commit)阶段。在协调阶段,React 会遍历 Fiber 树,找出需要更新的节点,并将这些节点标记为待处理。这个阶段可以被中断和重启,以便在浏览器空闲时间内执行。提交阶段则是将在协调阶段找到的待处理节点实际更新到 DOM 上,这个阶段是不能被中断的,以确保页面的一致性。
- 任务优先级:Fiber 架构引入了任务优先级的概念,对于不同类型的更新(如用户交互、动画、数据获取等),React 会分配不同的优先级。高优先级的任务会优先执行,从而提高用户体验。
通过 Fiber 架构,React 能够更好地管理组件的更新和渲染,提高了大型应用的性能和响应速度。
2,说说React Jsx转换成真实DOM过程?
React JSX 是一种语法糖,它允许我们使用类似于 HTML 的语法来描述组件的结构和属性。在 React 应用中,JSX 需要被转换为真实的 DOM 节点,以便在浏览器中进行渲染。以下是 JSX 转换为真实 DOM 的过程:
- JSX 转换:首先,需要将 JSX 代码转换为 JavaScript。这个过程通常是在构建阶段完成的,使用 Babel 等工具将 JSX 转换为 JavaScript。例如,
<div className="container"></div>
会被转换为React.createElement('div', {className: 'container'}, null)
。 - 创建 React 元素:
React.createElement()
函数会根据传入的参数创建一个 React 元素对象。这个对象包含了组件的类型(如 'div')、属性(如 className)和子元素等信息。这个 React 元素对象并不是真实的 DOM 节点,而是用于描述 DOM 结构的一种轻量级表示。 - 渲染:在 React 应用中,当组件的状态或属性发生变化时,React 会触发渲染过程。在这个过程中,React 会根据 React 元素对象创建对应的真实 DOM 节点,并将其插入到 DOM 树中。这个过程可能涉及到组件的生命周期方法,如
componentDidMount
、componentDidUpdate
等。 - 协调(Reconciliation):为了提高性能,React 使用一种名为协调的算法来避免不必要的 DOM 操作。在渲染过程中,React 会比较新旧两个 React 元素树(也称为虚拟 DOM 树),找出需要更新的部分,然后只更新这些部分。这种方式可以减少 DOM 操作的数量,从而提高应用的性能。
- 更新 DOM:根据协调算法找到的需要更新的部分,React 会执行实际的 DOM 操作,如创建、更新或删除 DOM 节点。这个过程称为提交(Commit)阶段,它是一个不能中断的过程,以确保页面的一致性。
总结一下,React JSX 转换为真实 DOM 的过程包括:JSX 转换、创建 React 元素、渲染、协调和更新 DOM。这个过程涉及到构建阶段的转换、虚拟 DOM 树的比较和实际的 DOM 操作。
3,说说 React 性能优化的手段有哪些?
React 提供了一些性能优化的手段,以提高应用的响应速度和性能。以下是一些常见的 React 性能优化方法:
-
使用
React.memo
:React.memo
是一个高阶组件,它可以让你的函数组件在接收到相同的属性时避免不必要的重新渲染。只有在组件的属性发生变化时,组件才会重新渲染。 -
使用
shouldComponentUpdate
或PureComponent
:对于类组件,可以通过实现shouldComponentUpdate
方法来避免不必要的重新渲染。这个方法接收新的属性和状态,返回一个布尔值,表示是否需要重新渲染。另外,可以使用React.PureComponent
作为组件的基类,它已经实现了一个基于浅比较的shouldComponentUpdate
方法。 -
分割代码:将大型应用拆分为多个较小的代码块,以实现按需加载。这可以通过动态
import()
语法和 Webpack 等构建工具来实现。这样可以减少应用的初始加载时间,提高性能。 -
优化事件处理函数:避免在渲染方法中创建新的事件处理函数,因为这会导致组件在每次渲染时都创建一个新的函数实例。可以将事件处理函数定义为类的方法或使用
useCallback
钩子来缓存函数。 -
使用
React.lazy
和Suspense
:React.lazy
允许你将组件的加载过程延迟到它们实际需要渲染时。结合React.Suspense
,可以在组件加载过程中显示一个加载指示器或占位符。 -
列表渲染优化:对于大型列表,可以使用虚拟列表(如
react-virtualized
库)来只渲染当前可见的列表项,从而提高性能。 -
使用
React.Fragment
:避免不必要的 DOM 节点,使用React.Fragment
来组合多个子元素,而不需要额外的 DOM 节点。 -
使用
useMemo
钩子:对于计算量较大的操作,可以使用useMemo
钩子来缓存计算结果,避免在每次渲染时都重新计算。 -
避免内联样式:尽量使用 CSS 类而不是内联样式,因为内联样式会在每次渲染时创建新的对象,可能导致不必要的重新渲染。
-
使用
React.Profiler
:使用React.Profiler
API 来分析组件的渲染性能,找出性能瓶颈并进行优化。
4, 让 useEffect 支持 async/await ?
- 创建一个异步函数(async...await 的方式),然后执行该函数。
useEffect(() => {
const asyncFun = async () => {
setPass(await mockCheck());
};
asyncFun();
}, []);
- 也可以使用 IIFE,如下所示:
useEffect(() => {
(async () => {
setPass(await mockCheck());
})();
}, []);
5,React 的 Diff 算法
- React 的 diff 算法用于高效地比较虚拟 DOM 树的变化,并将这些变化应用到实际的 DOM 中。
- 这个过程被称为“调和”(Reconciliation)
特点:
1,同层比较
React 将虚拟 DOM 树分层比较,只比较同一层级的节点,而不跨层级比较。这种策略大大减少了比较的复杂度,从 O(n^3) 降低到 O(n)。
- 不同类型的节点
不同类型的组件:如果新旧虚拟 DOM 树中对应位置的节点类型不同,React 会直接销毁旧节点及其子节点,并创建新节点及其子节点。
相同类型的组件:如果新旧节点类型相同,React 会保留该节点,并递归比较其子节点。
- Key 属性
在列表渲染中,React 通过 key
属性来标识每个节点。key
属性帮助 React 更高效地识别哪些节点发生了变化、被添加或被移除。
相同的 **key**
:React 会复用旧节点,并更新其属性和子节点。
不同的 **key**
:React 会认为这是一个新的节点,销毁旧节点并创建新节点。
- 节点操作
React 通过以下几种操作来更新 DOM:
插入:在新节点中存在,但在旧节点中不存在的节点会被插入。
删除:在旧节点中存在,但在新节点中不存在的节点会被删除。
移动:在新节点中存在且 key
相同,但位置不同的节点会被移动。
更新:在新节点中存在且 key
相同,位置也相同的节点会被更新。
具体步骤
- 树的分层比较:从根节点开始,逐层比较新旧虚拟 DOM 树。
- 节点类型比较:比较同一层级的节点类型,如果类型不同,直接替换整个节点。
- Key 属性比较:在列表渲染中,通过
key
属性来高效识别节点变化。 - 最小化操作:根据比较结果,生成最小的 DOM 操作集,更新真实 DOM。
转载自:https://juejin.cn/post/7399983106751348747