为什么说React的Virtual DOM概念比脏模型检查更有效?


372

我在(Pete Hunt:React:Rethinking Best Practices-JSConf EU 2013)上看到了一个React开发人员的演讲,演讲者提到对模型进行脏检查可能很慢。但是,由于虚拟DOM在大多数情况下应该比模型更大,难道计算虚拟DOM之间的差异实际上还没有表现得更好吗?

我真的很喜欢Virtual DOM(尤其是服务器端渲染)的潜在功能,但是我想知道所有的优点和缺点。


我想您也可以在youtube.com/watch?v=-DX3vJiqxm4上提及此话题,他在那儿特别谈到了基准测试。
inafalcao

Answers:


493

我是virtual-dom模块的主要作者,因此我也许可以回答您的问题。实际上这里有两个问题需要解决

  1. 我什么时候重新渲染? 答:当我观察到数据脏时。
  2. 如何有效地重新渲染?答案:使用虚拟DOM生成真实的DOM补丁

在React中,每个组件都有一个状态。这种状态就像您可以在敲除或其他MVVM样式库中发现的那样。本质上,React知道何时重新渲染场景,因为它能够观察到这些数据何时更改。脏检查比可观察到的检查要慢,因为必须定期轮询数据并递归检查数据结构中的所有值。相比之下,在状态上设置一个值将向侦听器发出某些状态已更改的信号,因此React可以简单地侦听状态上的更改事件并排队重新渲染。

虚拟DOM用于有效地重新渲染DOM。这实际上与对数据进行脏检查无关。您可以使用带有或不带有脏检查的虚拟DOM重新渲染。没错,在计算两个虚拟树之间的差异时会产生一些开销,但是虚拟DOM差异是关于了解DOM中需要更新的内容,而不是了解您的数据是否已更改。实际上,diff算法本身就是一个脏检查器,但是它用于查看DOM是否脏了。

我们旨在仅在状态更改时才重新渲染虚拟树。因此,使用可观察对象来检查状态是否已更改是防止不必要的重新渲染的有效方法,因为重新渲染会导致大量不必要的树差异。如果什么都没有改变,我们什么也不做。

虚拟DOM很好,因为它使我们可以像重新渲染整个场景一样编写代码。在幕后,我们要计算补丁操作,以更新DOM来查看我们的期望。因此,尽管虚拟DOM差异/补丁算法可能不是最佳解决方案,但它为我们提供了一种表达应用程序的好方法。我们只声明了我们想要的东西,React / virtual-dom将确定如何使您的场景看起来像这样。我们不必手动进行DOM操作,也不必对以前的DOM状态感到困惑。我们也不必重新渲染整个场景,这可能比修补它的效率低得多。


1
React会对组件道具进行脏检查吗?我问是因为没有setProps()函数。
bennlich 2014年


1
这样的例子是unnecessary re-renders什么?
vsync 2015年

9
当您说“因此,尽管虚拟DOM差异/补丁算法可能不是最佳解决方案”时,您是否想到了理论上更理想的解决方案?
CMCDragonkai 2015年

3
这似乎并不能回答这个问题。React要求您使用setState来指示状态已更改。如果能够做到this.state.cats = 99,则仍然需要进行脏检查以检查模型更改,就像Angular脏检查$ scope树一样。这不是两种技术速度的比较,只是陈述React不会进行脏检查,因为它具有Backbone样式设置器。
superluminary

133

我最近在这里阅读了有关React差异算法的详细文章:http : //calendar.perfplanet.com/2013/diff/。据我了解,使React快速的原因是:

  • 批量DOM读/写操作。
  • 仅有效更新子树。

与脏检查相比,IMO的主要区别在于:

  1. 模型脏检查:每次setState调用时,React组件都被显式设置为脏,因此这里不需要(数据)比较。对于脏检查,(模型的)比较总是在每个摘要循环中进行。

  2. DOM更新:DOM操作非常昂贵,因为修改DOM还将应用并计算CSS样式,布局。不必要的DOM修改所节省的时间可能比扩散虚拟DOM所花费的时间更长。

对于非平凡的模型(例如具有大量字段或大量列表的模型),第二点更为重要。复杂模型的一个字段更改将仅导致涉及该字段的DOM元素所需的操作,而不是整个视图/模板。


1
实际上,我也阅读了一些文章,因此,我现在(至少通常是在总体上)如何工作,我只是想弄清楚为什么它比对模型进行脏检查更有效。并且1)是的,它没有比较模型,但是确实比较了更大的虚拟dom 2)对模型的脏检查也使我们也能够只更新所需的内容(就像Angular一样)
Daniil 2014年

我相信只需要比较虚拟DOM中与已更改的组件相对应的部分,而脏检查会在每个摘要循环中针对每个作用域中的每个值进行,即使没有任何更改。如果更改了大量数据,则虚拟DOM的效率将降低,但对于较小的数据更改则不会。

1
说到Angular,由于观察者还可以在摘要时更改状态$scope.$digest,因此每个摘要周期要执行多次,因此它是完整数据比较的多次与部分虚拟DOM树比较的一次。
tungd

4
令人遗憾的是,有这么多聪明的开发人员发明了“山”来应对“慢”的DOM等,而不是集中我们的全部精力去仅仅修复浏览器本身,并一劳永逸地摆脱DOM慢。这就像利用全人类的资源来研究治疗癌症和改善患者生活的方法,而不仅仅是修复癌症本身。嘲笑。
vsync 2015年

@vsync DOM需要在屏幕上显示内容。虚拟DOM则没有。即使使用性能理想的DOM,创建虚拟DOM也会更快。
Jehan

75

我真的很喜欢Virtual DOM(尤其是服务器端渲染)的潜在功能,但是我想知道所有的优点和缺点。

-OP

React并不是唯一的DOM操作库。我鼓励您通过阅读Auth0的这篇文章来了解替代方法,其中包括详细的说明和基准。根据您的要求,在这里我将重点介绍它们的优缺点:

React.js的虚拟DOM

在此处输入图片说明

优点

  • 快速高效的“差异”算法
  • 多个前端(JSX,超标)
  • 轻巧到足以在移动设备上运行
  • 很多牵引力和思想分享
  • 无需React也可以使用(即作为独立引擎)

缺点

  • DOM的完整内存副本(更高的内存使用量)
  • 静态和动态元素之间没有区别

Ember.js的微光

在此处输入图片说明

优点

  • 快速高效的差分算法
  • 静态和动态元素之间的区别
  • 100%与Ember的API兼容(无需大量更新现有代码即可获得收益)
  • DOM的轻量级内存表示

缺点

  • 只能在Ember中使用
  • 仅一个前端可用

增量DOM

在此处输入图片说明

优点

  • 减少内存使用
  • 简单的API
  • 轻松集成许多前端和框架(从一开始就希望作为模板引擎后端)

缺点

  • 不如其他库快(这是有争议的,请参见下面的基准测试)
  • 更少的思想分享和社区使用

对我来说,ReactJS的DOM操作的表示似乎有点不足。ReactJS的虚拟DOM是完全改变的,而不是实际的DOM-对吗?我正在查看引用的文章所引用的原始文章,这就是我所看到的-teropa.info/images/onchange_vdom_change.svgteropa.info/blog/2015/03/02/…–
smile.al.d.way

35

这是React团队成员SebastianMarkbåge的评论,阐明了一些观点:

React对输出进行区分(这是已知的可序列化格式,DOM属性)。这意味着源数据可以是任何格式。它可以是不可变的数据结构,也可以是闭包内部的状态。

Angular模型不会保留参照透明性,因此本质上是可变的。您对现有模型进行变异以跟踪更改。如果您的数据源每次都是不可变数据或新的数据结构(例如JSON响应)怎么办?

脏检查和Object.observe不适用于关闭范围状态。

显然,这两件事非常限制了功能模式。

此外,当模型复杂度增加时,进行脏跟踪变得越来越昂贵。但是,如果仅像在React之类的视觉树上进行比较,则它不会增长太多,因为您可以在任何给定点显示在屏幕上的数据量受到UI的限制。皮特(Pete)的上面的链接涵盖了更多的性能好处。

https://news.ycombinator.com/item?id=6937668


2
实际上是关于最后一段:这应该是错误的:模型大于虚拟dom,因为对于每个模型值,在大多数情况下,至少有一个虚拟dom元素(通常不止一个)。为什么要显示未显示的模型?
丹尼尔,2014年

2
对缓存的集合进行分页。
kentor 2015年

-2

Virtual Dom不是通过react发明的。它是HTML dom的一部分。它是轻量级的,并且与浏览器特定的实现细节分离。

我们可以将虚拟DOM视为React的HTML DOM的本地和简化副本。它允许React在这个抽象的世界中进行计算,并跳过“真实的” DOM操作,这些操作通常很慢且特定于浏览器。实际上,DOM和虚拟DOM之间并没有很大的区别。

以下是使用虚拟Dom的要点(ReactJS中的虚拟DOM):

当您这样做时:

document.getElementById('elementId').innerHTML = "New Value" Following thing happens:
  1. 浏览器需要解析HTML
  2. 删除elementId的子元素
  3. 用新值更新DOM值
  4. 重新计算父母和孩子的CSS
  5. 更新布局,即每个元素在屏幕上的精确坐标
  6. 遍历渲染树并将其绘制在浏览器显示屏上

重新计算CSS和更改的布局使用复杂的算法,它们会影响性能。

以及更新DOM属性。价值观。它遵循一种算法。

现在,假设您直接更新DOM 10次,那么以上所有步骤将一个接一个地运行,并且更新DOM算法将花费一些时间来更新DOM值。

这就是为什么Real DOM比虚拟DOM慢的原因。


3
关于示例,如果直接或通过虚拟dom修改dom,那么最后两种情况下都将更改dom。
magallanes

是的,在这两种情况下,我们都在更新dom,但在虚拟dom的情况下,它仅更新密钥(唯一地由与React不同的算法定义的)字段或元素标签。而更新dom会完全更新或刷新整个dom。
Hemant Nagarkoti

11
我从hackernoon.com/virtual-dom-in-reactjs-43a3fdb1d130看了这篇文章。如果您不是作者,那么最好指出来源。
井冈

2
“这就是为什么Real DOM比虚拟DOM慢的原因。” 不,先生,你只是错了。
Roecrew
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.