为什么React的Virtual DOM概念被认为比脏模型检查更具性能?

Dan*_*iil 357 javascript dom reactjs virtual-dom

我在http://www.youtube.com/watch?v=x7cQ3mrcKaY上看到了React dev的演讲,并且发言人提到模型的脏检查可能很慢.但是,由于虚拟DOM在大多数情况下应该比模型更大,所以虚拟DOM之间的差异实际上并不是很差.

我非常喜欢Virtual DOM的潜在力量(特别是服务器端渲染),但我想知道所有的优点和缺点.

Mat*_*sch 483

我是虚拟dom模块的主要作者,所以我或许可以回答你的问题.事实上,这里有两个问题需要解决

  1. 我什么时候重新渲染? 答:当我发现数据很脏时.
  2. 如何有效地重新渲染?答:使用虚拟DOM生成真正的DOM补丁

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

虚拟DOM用于有效地重新呈现DOM.这与脏检查数据无关.您可以使用带有或不带脏检查的虚拟DOM重新渲染.你是对的,计算两个虚拟树之间的差异有一些开销,但虚拟DOM差异是关于理解DOM中需要更新的内容,而不是你的数据是否发生了变化.事实上,diff算法本身就是一个脏检查器,但它用于查看DOM是否是脏的.

我们的目标是仅在状态发生变化时重新渲染虚拟树.因此,使用observable来检查状态是否已更改是防止不必要的重新渲染的有效方法,这会导致大量不必要的树差异.如果什么都没有改变,我们什么都不做.

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

  • 当你说"那么虚拟DOM差异/补丁算法可能不是最佳解决方案"时,你是否考虑过理论上更优的解决方案? (9认同)
  • 这似乎不能回答这个问题.React要求您使用setState来表示状态已更改.如果你能够执行`this.state.cats = 99`,你仍然需要进行脏检查以检查模型更改,就像Angular dirty检查$ scope树一样.这不是两种技术的速度比较,它只是一个声明React不进行脏检查,因为它有一个Backbone样式的setter. (3认同)
  • 有一个setProps:http://facebook.github.io/react/docs/component-api.html#setprops (2认同)
  • 这种“不必要的重新渲染”的例子是什么? (2认同)

tun*_*ngd 132

我最近在这里阅读了一篇关于React的diff算法的详细文章:http://calendar.perfplanet.com/2013/diff/.根据我的理解,React的快速之处在于:

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

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

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

  2. DOM更新:DOM操作非常昂贵,因为修改DOM也会应用和计算CSS样式,布局.从不必要的DOM修改中节省的时间可能比传播虚拟DOM所花费的时间长.

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

  • 令人伤心的是,许多聪明的开发人员发明了"大量"技巧来处理"缓慢"的DOM等等,而不是将我们的注意力集中在修复浏览器本身并一劳永逸地摆脱DOM缓慢.这就像利用全人类的资源来研究处理癌症和改善病人生活的方法,而不仅仅是修复癌症本身.嘲笑. (4认同)

fal*_*lla 73

我非常喜欢Virtual DOM的潜在力量(特别是服务器端渲染),但我想知道所有的优点和缺点.

- OP

React不是唯一的DOM操作库.我鼓励您通过阅读Auth0中的这篇文章来理解其他选择,其中包括详细的解释和基准.你会问:我会在这里强调一下它们的优点和缺点:

React.js的虚拟DOM

在此输入图像描述

PROS

  • 快速高效的"差异"算法
  • 多个前端(JSX,hyperscript)
  • 轻巧,足以在移动设备上运行
  • 很多牵引力和思想分享
  • 可以在没有React的情况下使用(即作为独立引擎)

缺点

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

Ember.js'微光

在此输入图像描述

PROS

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

缺点

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

增量DOM

在此输入图像描述

PROS

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

缺点

  • 没有其他库那么快(这是有争议的,请参阅下面的基准)
  • 减少思想共享和社区使用


Sop*_*ert 36

以下是React团队成员SebastianMarkbåge的评论,其中有一些说明:

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

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

脏检查和Object.observe对闭包范围状态不起作用.

这两件事显然对功能模式非常有限.

此外,当您的模型复杂性增加时,进行脏跟踪变得越来越昂贵.但是,如果你只在视觉树上进行差异化,比如React,那么它就不会增长太多,因为你能够在任何给定点在屏幕上显示的数据量受到UI的限制.Pete上面的链接涵盖了更多的性能优势.

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

  • 实际上关于最后一段:它应该是错误的:模型比虚拟dom更大,因为对于每个模型值,(在大多数情况下)存在至少一个虚拟dom元素(并且通常远多于一个).为什么我想要未显示的型号? (2认同)
  • Paginating缓存集合. (2认同)