likes
comments
collection
share

React幕后的工作机制:深入理解 React 的虚拟 DOM、Fiber 和 Reconciliation每当我们新建

作者站长头像
站长
· 阅读数 29

每当我们新建一个新的React程序时,你可能会注意到index.js(在vite中创建则为main.jsx)中有这样一段代码:

...
import ReactDOM from "react-dom/client";  
...

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>
);

这个ReactDOM是何方神圣,有何作用? createRoot 的作用又是什么呢?

createRoot 实际上创建一个类似于浏览器 DOM 的虚拟 DOM 结构,也就是我们常说的React virtual DOM (或许更应该叫做 React Element Tree )

为什么会需要一个虚拟DOM?

🤔 你的心中可能会有些许疑问,为什么需要一个虚拟的DOM?直接上手操作真实DOM不好吗?

客官莫急,且听我徐徐道来。

Virtual DOM (REACT ELEMENT TREE)

概念

虚拟 DOM 是一个包含了所有 React 元素的树状结构。这个树状结构由组件树中所有实例的 React 元素组成。换句话说,当你写一个 React 组件时,React 会创建一个表示这些组件的虚拟 DOM 树。

特点

React 的虚拟 DOM 可以很容易地创建多个树结构。这是因为虚拟 DOM 只是 JavaScript 对象的表示,所以它的创建速度非常快,并且不会像实际 DOM 那样昂贵。

值得一提

Virtual DOM 与 “Shadow DOM”无关: Virtual DOM 与 “Shadow DOM” 是不同的概念。Shadow DOM 是一种用于封装 web 组件的技术,而Virtual DOM 主要用于 React 中的高效渲染。

现在你已经知道了传说中的虚拟DOM实际上只是一个普通的对象,这样看来其实没什么大不了的对吧?并没有人们口中的那么晦涩难懂。这就是为什么React团队实际上淡化了虚拟DOM这个名词的含义,官方文档中也不再提到虚拟DOM这个名字了(下文有更多关于这个的解释)。


在了解虚拟DOM之后,我们来解答疑惑,为什么不直接操作实际DOM树:

因为...

  • 这样做效率低且浪费资源

    1. 直接写入 DOM 是相对较慢的:操作实际 DOM 是一个开销较大的过程。DOM 的变更往往需要浏览器重新计算布局、重绘和重新渲染,尤其是在大型应用中,这样的操作会导致明显的性能问题。
    2. 重渲染通常只需要更新 DOM 的一小部分:在大多数情况下,状态变化只会影响 DOM 的一部分。因此,完全重新渲染整个 DOM 是没有必要的。
  • React 会尽可能重用现有的 DOM:为了提高性能,React 会尽可能重用已经存在的 DOM 元素,而不是每次都重新创建和插入新的 DOM 元素。

那么聪明的你可能又会问了,如此神奇的比较和重用,React是如何做到这一点的?那么接下来让我们引入一个概念——协调机制。

协调机制 (Reconciliation)

React中的协调器(Reconciler)——Fiber

概念

在应用的初次渲染期间(Initial render),Fiber会在虚拟DOM树的基础上构建一个Fiber tree,它是一个数据结构,代表了整个应用的组件树。每个 Fiber 对象都是树中的一个节点,用来描述一个 React 组件实例的状态和效果,它保存了与该组件相关的所有信息,包括组件的类型、副作用、props、state、上下文甚至工作队列。

特点

Fiber tree并不会在每次重渲染中重新创建,所以它永远不会被销毁,并且,它是一个可变的数据结构,当在初次渲染中被创建之后,它只会在未来的协调过程中一次又一次的变异修改(mutate)。

Fiber tree中的元素排列并不像虚拟DOM中的那样完全呈现父子关系,相反,更多的是兄弟关系,如下图

虚拟DOM树

React幕后的工作机制:深入理解 React 的虚拟 DOM、Fiber 和 Reconciliation每当我们新建

Fiber tree

React幕后的工作机制:深入理解 React 的虚拟 DOM、Fiber 和 Reconciliation每当我们新建

最后也是最重要的一点: Fiber的工作可以异步完成,渲染过程可以被分割成多个小块,任务可以被优先处理,并且工作可以暂停复用丢弃,这使得并发特性如 Suspense 或过渡效果成为可能,并且长时间的渲染不会阻塞 JavaScript 引擎

工作原理

等等,这个Fiber tree听起来好像和虚拟DOM有点子像,那么他们的区别是啥呢?

实际上,Fiber tree是和虚拟DOM相辅相成来最终达成提交渲染操作到真实DOM上的。在一个组件触发更新后,会有一个全新的虚拟DOM树被创建,随后就会与当前界面上 (未更新状态时) 的Fiber tree进行协调(Reconciliation)差异比较(Diffing),这个过程是在React中的协调器(Fiber)中进行的,这就是为什么我们会有一个Fiber tree。这个协调的最终结果是产生一个更新过后的Fiber tree,并且产生一个DOM更新操作的列表**("list of effects")**。注意哦,不是一个全新的Fiber tree,是一个更新过后的Fiber tree——React是非常勤俭持家的,多一点性能开销都是浪费。

总结

❤️ 协调(Reconciliation) :协调是 React 用来决定哪些 DOM 元素需要插入、删除或更新的过程。协调的最终结果是产生一个基于新状态的DOM更新操作列表**("list of effects")**,最后在提交阶段中按照列表中的信息去实际操作DOM。

值得一提

协调器可以说是React的引擎,像是React的心脏。也正是因为协调器我们永远不会直接接触DOM,只是简单的告诉React根据当前的状态更新,下一个UI的快照是什么样的。

最后我们讲讲上面提到的 ReactDOM 包,我了解到的很有意思的一点是,在重渲染的四个阶段中,第三个阶段——提交阶段——并不是React的工作,而是一个被称为ReactDOM的 主机(hosts) 去接管提交操作(我想你肯定也注意到了index.js/main.jsx里有一个导入ReactDOM包),并且这里使用ReactDOM作为主机也只是因为我们用React做前端网页,所以提交阶段的结果是DOM操作列表。

实际上 提交阶段的结果是主机中使用的任何元素的操作列表 ,比如你可以使用React Native去做安卓和IOS开发,Remotion去做视频开发等等等等,你只需要导入对应的包去取代 ReactDOM 即可。

并且从这个角度看虚拟DOM也没有什么特殊意义,上面也提到了React团队也不再喜欢使用虚拟DOM这个术语,而是叫 React element tree


结语

这一部分可以说是整个React中比较晦涩难懂的一部分了,但是当你仔细研究了后会发现实际上也没有别人说的那么难懂,希望这篇文章的分享可以让大家对其中的一些原理理解更加透彻,如果文章有错误的地方也还请指正,最后谢谢大家的阅读~

转载自:https://juejin.cn/post/7404274946434400268
评论
请登录