我在(Pete Hunt: React: Rethinking best practices—JSConf EU 2013)上看过一个反应开发演讲,演讲者提到模型的脏检查可能会很慢。但是,在大多数情况下,虚拟DOM应该比模型更大,因此计算虚拟DOM之间的差异实际上不是性能更差吗?
我真的很喜欢虚拟DOM的潜在功能(特别是服务器端渲染),但我想知道所有的优点和缺点。
我最近读了一篇关于React的diff算法的详细文章:http://calendar.perfplanet.com/2013/diff/。根据我的理解,React快速的原因是:
与脏检查相比,IMO的主要区别是:
当setState被调用时,React组件被显式地设置为dirty,因此这里不需要(数据的)比较。对于脏检查,(模型的)比较总是发生在每个摘要循环中。
setState
DOM操作是非常昂贵的,因为修改DOM也将应用和计算CSS样式,布局。从不必要的DOM修改中节省的时间可能比区别虚拟DOM所花费的时间要长。
第二点对于具有大量字段或大列表的非平凡模型更为重要。复杂模型的一个字段更改将只导致涉及该字段的DOM元素所需的操作,而不是整个视图/模板。
下面是React团队成员Sebastian Markbåge的一篇评论,它揭示了一些问题:
React对输出进行区分(这是一种已知的可序列化格式,DOM属性)。这意味着源数据可以是任何格式。它可以是闭包中的不可变数据结构和状态。 Angular模型并没有保持引用透明性,因此本质上是可变的。您更改现有模型以跟踪更改。如果数据源是不可变数据或每次都是新的数据结构(如JSON响应),该怎么办? 脏检查和对象。观察在闭包作用域状态下不工作。 显然,这两点对功能模式有很大的限制。 此外,当您的模型复杂性增加时,执行脏跟踪的成本也会越来越高。然而,如果你只在视觉树上做差分,比如React,那么它就不会增长太多,因为你在任何给定点上能够在屏幕上显示的数据量都受到ui的限制。Pete上面的链接涵盖了更多的性能好处。
React对输出进行区分(这是一种已知的可序列化格式,DOM属性)。这意味着源数据可以是任何格式。它可以是闭包中的不可变数据结构和状态。
Angular模型并没有保持引用透明性,因此本质上是可变的。您更改现有模型以跟踪更改。如果数据源是不可变数据或每次都是新的数据结构(如JSON响应),该怎么办?
脏检查和对象。观察在闭包作用域状态下不工作。
显然,这两点对功能模式有很大的限制。
此外,当您的模型复杂性增加时,执行脏跟踪的成本也会越来越高。然而,如果你只在视觉树上做差分,比如React,那么它就不会增长太多,因为你在任何给定点上能够在屏幕上显示的数据量都受到ui的限制。Pete上面的链接涵盖了更多的性能好处。
https://news.ycombinator.com/item?id=6937668
我是virtual-dom模块的主要作者,所以我可能能够回答你的问题。事实上,这里有两个问题需要解决
在React中,每个组件都有一个状态。这种状态就像你在knockout或其他MVVM风格的库中发现的一个可观察对象。本质上,React知道当来重新渲染场景,因为它能够观察到该数据何时发生变化。脏检查比可观察数据慢,因为您必须以定期的间隔轮询数据,并递归地检查数据结构中的所有值。相比之下,在状态上设置一个值将向侦听器发出某个状态已更改的信号,因此React可以简单地侦听状态上的更改事件并排队重新呈现。
虚拟DOM用于高效地重新呈现DOM。这实际上与检查数据无关。您可以使用虚拟DOM重新呈现,有或没有脏检查。您是对的,计算两个虚拟树之间的差异存在一些开销,但是虚拟DOM差异是关于了解DOM中需要更新的内容,而不是您的数据是否已经更改。实际上是diff算法本身就是一个脏检查器,但它被用来检查DOM是否脏。
我们的目标是只在状态改变时重新渲染虚拟树。因此,使用一个可观察对象来检查状态是否已经改变是一种有效的方法来防止不必要的重新渲染,这将导致大量不必要的树差异。如果没有改变,我们什么都不做。
虚拟DOM很好,因为它让我们编写代码时就像重新渲染整个场景一样。在幕后,我们希望计算一个补丁操作,以更新DOM,使其看起来像我们期望的那样。因此,虽然虚拟DOM diff/patch算法可能不是最优解,它给了我们一个很好的方式来表达我们的应用程序。我们只是声明我们想要什么,React/virtual-dom会解决如何让你的场景看起来像这样。我们不必手动操作DOM,也不必对之前的DOM状态感到困惑。我们也不需要重新渲染整个场景,这可能比修补它效率低得多。
我真的很喜欢虚拟DOM的潜在力量(特别是 服务器端渲染),但我想知道所有的优点和缺点 ——凤凰社
——凤凰社
React并不是唯一的DOM操作库。我鼓励你通过阅读包含详细解释和基准测试的文章来自Auth0来理解替代方案。我将在这里强调他们的优点和缺点,正如你所问的:
React.js的虚拟DOM 优点 快速高效的“差分”算法 多个前端(JSX,超脚本) 足够轻,可以在移动设备上运行 很多牵引力和思想份额 可以在没有React的情况下使用(即作为一个独立的引擎) 缺点 DOM的完整内存副本(更高的内存使用量) 静态和动态元素没有区别 Ember.js的线 优点 快速高效的差分算法 区分静态和动态元素 100%兼容烬的API(你得到的好处没有重大更新你的现有代码) DOM的轻量级内存表示 缺点 只能在烬城使用 只有一个前端可用 增量DOM 优点 减少内存使用 简单的API 轻松集成许多前端和框架(从一开始就意味着作为模板引擎后端) 缺点 不如其他库快(这是有争议的,请参阅下面的基准测试) 较少的思想分享和社区使用
我们可以把虚拟DOM看作是React对HTML DOM的本地简化副本。它允许React在这个抽象的世界中进行计算,而跳过“真正的”DOM操作,这些操作通常很慢,而且是特定于浏览器的。实际上,DOM和VIRTUAL DOM之间没有太大的区别。
下面是使用Virtual Dom的原因(源ReactJS中的虚拟DOM):
当你这样做时: document.getElementById('elementId').innerHTML = "New Value" Following thing happens: 浏览器需要解析HTML 它删除了elementId的子元素 用新值更新DOM值 重新计算父节点和子节点的css 更新布局,即每个元素在屏幕上的精确坐标 遍历渲染树并在浏览器显示上绘制它 重新计算CSS和改变布局使用复杂的算法和 它们影响性能。
当你这样做时:
document.getElementById('elementId').innerHTML = "New Value" Following thing happens:
重新计算CSS和改变布局使用复杂的算法和 它们影响性能。
以及更新DOM属性。值。它遵循一种算法。
现在,假设您直接更新DOM 10次,那么上述所有步骤将逐一运行,更新DOM算法将花费时间来更新DOM值。
这就是为什么真实DOM比虚拟DOM慢。
在React中,每个组件都有一个状态。这种状态就像你在knockout或其他MVVM风格的库中发现的一个可观察对象。从本质上讲,React知道什么时候重新渲染场景,因为它能够观察到这些数据何时发生变化。脏检查比可观察数据慢,因为您必须以定期的间隔轮询数据,并递归地检查数据结构中的所有值。相比之下,在状态上设置一个值将向侦听器发出某个状态已更改的信号,因此React可以简单地侦听状态上的更改事件并排队重新呈现。虚拟DOM用于高效地重新呈现DOM。这实际上与检查数据无关。您可以使用虚拟DOM重新呈现,有或没有脏检查。您是对的,计算两个虚拟树之间的差异存在一些开销,但是虚拟DOM差异是关于了解DOM中需要更新的内容,而不是您的数据是否已经更改。事实上,diff算法本身是一个脏检查器,但它被用来检查DOM是否脏。