React 的 Virtual DOM 到底有多快?

Posted

技术标签:

【中文标题】React 的 Virtual DOM 到底有多快?【英文标题】:How exactly is React's Virtual DOM faster? 【发布时间】:2020-07-29 10:10:52 【问题描述】:

我知道 React 的虚拟 DOM 更快有两个论据 -

    它只更新那些实际需要更新的元素(使用差异)。

    它批量更新,因此我们只更新一次真实的 DOM。因此,重绘也只进行一次,否则会进行多次。

我对这两点都有疑问 -

    据我所知,所有现代浏览器的效率都足以仅更新 DOM 中所需的元素。例如,如果我有两个“p”标签,并且我通过单击按钮更改了其中一个 p 标签中的文本,那么 safari 只会更新那个 p 标签(我已经使用油漆闪烁验证了这一点)。那么如果第 1 点已经被浏览器实现了,那么它有什么优势呢?

    React 究竟是如何批处理更新的?最终 React 还必须使用 DOM api 来更新真实的 DOM。那么为什么如果我们直接使用 DOM api 则更改不会被批处理,而当 React 使用它时,它们会被批处理?

【问题讨论】:

这有助于解释吗? reactjs.org/docs/reconciliation.html @DrewReese 它没有解决我提到的问题。它只解释了差异过程。但它没有提到这是一个优势。它也没有讨论批处理。 差异化是优势;更新虚拟 DOM 更快,因为在内存中更新比在屏幕上更新更快。 @David 解释得很好。对于 #2,react 是开源的,查看代码以了解它们如何批量更新。 @DrewReese 我知道内存中的更新速度更快。但最终你必须在屏幕上更新。这使得内存中的更新只是一种开销。论点是它有助于仅更新那些已更改的元素。我是说浏览器已经这样做了。那么,react 究竟做了什么来提高性能呢? 是的,但不是真的。浏览器只重绘更新的内容。 React 对这些更新进行批处理,因此它不会为 DOM 中更改的 每个 小元素来回切换。正如大卫指出的那样,成本是重新粉刷。重绘的次数越少,框架的效率就越高。 【参考方案1】:

我找到了问题的答案。

关键是要理解 Virtual DOM 的用途。

首先我们必须看看 React 采用什么方法来渲染组件。

不同的 javascript 框架采用不同的方法来检测数据模型中的变化并将其呈现在视图上。

考虑 AngularJS。当我们在 Angular 模板中引用我们的数据时,例如在像 foo.x 这样的表达式中,Angular 不仅会呈现该数据,还会为该特定值创建一个观察者。每当我们的应用程序发生任何事情(点击事件、HTTP 响应、超时)时,所有的观察者都会运行。如果观察者中的值已更改,则该值将在 UI 中重新呈现。通过运行所有的观察者,AngularJS 本质上是找出它需要在哪里进行更改。运行这些观察者的过程称为脏检查。

React 采用了不同的方法。每当 React 组件中的状态发生变化时,React 会从头开始重新渲染整个 UI(使用更新的状态),而不是找出在哪里进行更改(如 AngularJS)。

但是这种 React 方法有一个问题。重新渲染整个 UI 意味着重新渲染整个 DOM 树。这是一个问题,因为 DOM 更新是一个缓慢的过程(由于回流和重绘)。

这就是 React 的 Virtual DOM 的用武之地。Virtual DOM 只是 Real DOM 以 javascript 对象的形式表示。它只是存在于内存中的普通 javascript 对象的树形数据结构。与 Real DOM 相比,Virtual DOM 的渲染速度要快得多,因为它永远不会在屏幕上渲染(无需进行回流或重绘)。

那么 Virtual DOM 是如何解决这个问题的呢?当我们加载我们的应用程序时,React 会创建一个虚拟 DOM,它是真实 DOM 的精确虚拟副本。每当组件中的状态发生变化时,React 不会重新渲染整个 Real DOM,而是渲染一个全新的 Virtual DOM(具有更新的状态)。然后它在旧的 Virtual DOM(Real DOM 的初始副本)和这个新的 Virtual DOM(在状态更改后渲染)之间进行比较,以找出它们之间的变化,并且它只做 Real DOM 中的那些变化。通过这种方式,整个 UI 被重新渲染(通过渲染一个全新的 Virtual DOM),但只在 Real DOM 中完成了所需的最少更改。

所以当说“使用 Virtual DOM React 只更新那些需要更新的元素”(我的问题中的第 1 点)时,这意味着在 Virtual DOM 的帮助下,React 正在克服其自身方法的局限性(从头开始渲染整个 UI 的方法)。

这个answer 也解释了同样的概念。

我看到一些回答说使用 React 操作 DOM 比使用 DOM api 更快,因为 DOM api 重新渲染整个 DOM 树,而 React 只重新渲染 DOM 树中需要更改的部分.这不是真的。所有现代浏览器都足够高效,只更新 DOM 树中需要更改的部分。这可以在浏览器的开发者工具中使用paint flashing 来验证(另见answer 和answer)。即使我们假设 DOM api 确实重新渲染了整个 DOM 树,但这种推理仍然是错误的,因为 React 本身的内部代码必须使用 DOM api 来更新 DOM。如果 DOM api 确实重新渲染了整个 DOM 树,那么 React 也会重新渲染整个 DOM 树,因为最终它也使用 DOM api 来更新 DOM。

  至于第二点,React 实际上让我们更容易进行批处理。

在 React 中,虽然读取是在 Real DOM 上完成的,但写入(状态更改)不是在 Real DOM 上完成的。相反,写入是排队的。然后当我们所有的读写都处理完后,一个新的 Virtual DOM 就会基于这些写来构建。然后在新旧 Virtual DOM 之间进行差异化,然后 React 将所需的更改写入 Real DOM 以更新它。因此,最终 Real DOM 上的所有写入都在一个回流中一起完成。

但是我们也可以在没有 React 的情况下手动编写代码,首先完成所有读取,然后完成所有写入。 React 使批处理更容易,因为使用 React 我们不必关心一起进行读取和写入,React 会自动为我们批处理写入。所以 React 不会让事情变得很快。它使事情变得更容易。

  总之,我们可以说 React 实际上并不快。这更容易。正如 Pete Hunt 在video 中所说,“React 并不神奇。就像您可以使用 C 进入汇编器并击败 C 编译器一样,您可以进入原始 DOM 操作和 DOM API 调用,如果您愿意,也可以击败 React。但是,使用 C 或 Java 或 JavaScript 是一个数量级的性能改进,因为您不必担心...关于平台的细节。使用 React,您可以构建应用程序而无需考虑性能,并且默认状态很快。”。

Rich Harris 的 post 还指出“虚拟 DOM 很快”是一个神话。

【讨论】:

一个简单的具体例子是很长的列表或表格,有和没有key 属性。浏览器永远无法对此进行优化,因为这不在浏览器的工作范围内。你总是需要一个抽象来实现优化。 @user120242 能详细解释一下这个关键优化吗? 我认为我的问题试图提出的观点(你承认它有一些道理)是 Pete Hunt 在视频中所说的 - “React 不是魔法。你总是可以编写汇编代码击败 C。以类似的方式,您始终可以使用原始 DOM 操作和 DOM api 来击败 React”(释义)。但 React 的目的绝不是打败 Vanilla JS。 React 的目的是将维护状态更改的工作从开发人员转移到库。 浏览器并没有真正“批量”更改,就像在这些框架中一样,DOM 仍然只是浏览器的普通 js 对象。它的作用是将 reflow 延迟到下一次绘制(以显示器刷新率发生)。只有当 js 需要计算值时,它才会同步强制回流。见***.com/questions/47342730/… @anonymous_siva 在 React 中,setState 调用是异步的,它们是batched 一起。因此,您不必担心它们会被批处理,因为 React 会为您做到这一点。【参考方案2】:

一旦 React 知道哪些虚拟 DOM 对象发生了变化,那么 React 只会更新真实 DOM 中的那些对象。与直接操作真实 DOM 相比,这使得性能要好得多。这使得 React 作为高性能 JavaScript 库脱颖而出。

关于批量更新:

React 遵循批量更新机制来更新真实的 DOM。因此,导致性能提高。 这意味着对真实 DOM 的更新是分批发送的,而不是为每次状态更改发送更新。

UI 的重绘是最昂贵的部分,React 有效地确保真正的 DOM 只接收批量更新来重绘 UI。

【讨论】:

“如何”让性能更好?即使没有 React,浏览器也只会更新那些需要更改的对象。反应带来了什么? 粗体字没有回答吗? 关于批量更新,反应用来“批量”更新的机制到底是什么。它如何使用 DOM api 批量发送更新? "然后 React 只更新真实 DOM 中的那些对象。与直接操作真实 DOM 相比,这使得性能要好得多。"不是在粗体文本的上下文中说的,而是在仅更新更改的对象的上下文中说的。 @chetanraina 从历史上看,当 React 被引入时,浏览器中的那些优化并不像现在这样高效。后来的浏览器,随着 React 引入的虚拟 DOM 概念的引入,创建了减轻性能差异的优化。

以上是关于React 的 Virtual DOM 到底有多快?的主要内容,如果未能解决你的问题,请参考以下文章

快速排序算法到底有多快?

手把手教你快速排序算法,看看它到底有多快?

手把手教你快速排序算法,看看它到底有多快?

王思聪花了100万的组装的电脑,网速到底有多快?

ClickHouse 到底有多快? 200亿行数据 count,group 的响应时间

Java 17到底有多快?