为什么说React的虚拟DOM概念比脏模型检查更高效?
我在http://www.youtube.com/watch?v=x7cQ3mrcKaY上看到了一个React开发人员的演讲,演讲者提到模型的肮脏检查可能会很慢。 但是,由于在大多数情况下,虚拟DOM应该比模型更大,所以不计算虚拟DOM之间的差异实际上甚至更less。
我真的很喜欢虚拟DOM的潜在力量(特别是服务器端渲染),但我想知道所有的优点和缺点。
我是虚拟DOM模块的主要作者,所以我可以回答你的问题。 其实有两个问题需要在这里解决
- 我什么时候重新渲染? 答:当我观察到数据很脏时。
- 我如何有效地重新渲染? 答:使用虚拟DOM来生成一个真正的DOM补丁
在React中,你的每个组件都有一个状态。 这个状态就像在knockout或其他MVVM风格的库中可以看到的那样。 本质上,React知道何时重新渲染场景,因为它能够观察这个数据何时改变。 脏检查比observables慢,因为您必须定期轮询数据并recursion检查数据结构中的所有值。 相比之下,在状态上设置一个值会告诉一个监听者一些状态已经改变,所以React可以简单地监听状态的变化事件并排队重新渲染。
虚拟DOM用于高效地重新呈现DOM。 这实际上并不涉及检查你的数据。 您可以使用虚拟DOM重新呈现,无论是否进行脏检查。 你是对的,在计算两个虚拟树之间的差异时有一些开销,但是虚拟DOM差异是关于理解DOM中需要更新的内容而不是你的数据是否已经改变。 实际上, diffalgorithm本身就是一个脏检查器,但是它用来查看DOM是否脏。
我们的目标是只在状态改变时重新渲染虚拟树。 因此,使用observable来检查状态是否已经改变是防止不必要的重新渲染的有效方法,这将导致大量不必要的树差异。 如果什么都没有改变,我们什么也不做。
一个虚拟的DOM是好的,因为它让我们编写我们的代码,就好像我们正在重新渲染整个场景一样。 在幕后,我们想要计算一个补丁操作,更新DOM来看看我们的期望。 所以,虽然虚拟DOM差异/补丁algorithm可能不是最佳解决scheme ,但它给了我们一个很好的方式来expression我们的应用程序。 我们只是声明我们想要的,React / virtual-dom将解决如何让你的场景看起来像这样。 我们不必手动操作DOM,或者对以前的DOM状态感到困惑。 我们不必重新渲染整个场景,这可能比修补效率低得多。
我最近在这里阅读了关于React的diffalgorithm的详细文章: http : //calendar.perfplanet.com/2013/diff/ 。 据我所知,使React快速的是:
- 批量DOM读/写操作。
- 只有高效的子树更新。
与肮脏检查相比,IMO的主要差异是:
-
模型脏检查 :每当
setState
被调用时,React组件被显式地设置为脏,所以这里不需要比较(数据)。 对于脏检查,比较(模型)总是发生在每个摘要循环中。 -
DOM更新 :DOM操作非常昂贵,因为修改DOM也会应用和计算CSS样式,布局。 从不必要的DOM修改中节省的时间可以比花费在虚拟DOM上的时间更长。
第二点对于诸如具有大量字段或大列表的非平凡模型更为重要。 复杂模型的一个字段变化只会导致涉及该字段的DOM元素所需的操作,而不是整个视图/模板。
我真的很喜欢虚拟DOM的潜在力量(特别是服务器端渲染),但我想知道所有的优点和缺点。
– OP
React不是唯一的DOM操作库。 我鼓励您通过阅读Auth0中的这篇文章来了解其他方法,包括详细的解释和基准。 我会在这里强调他们的优点和缺点,正如你所问:
React.js的虚拟DOM
PROS
- 快速高效的“差异化”algorithm
- 多个前端(JSX,hyperscript)
- 轻量级足以在移动设备上运行
- 大量的牵引力和思想共享
- 可以使用没有反应(即作为一个独立的引擎)
缺点
- 完整的DOM内存拷贝(更高的内存使用)
- 静态和dynamic元素没有区别
Ember.js'微光
PROS
- 快速有效的差异化algorithm
- 静态和dynamic元素的区分
- 与Ember的API 100%兼容(您可以获得优势,而不必对现有代码进行重大更新)
- DOM的轻量级内存中表示
缺点
- 只能用于Ember
- 只有一个前端可用
增量DOM
PROS
- 减less内存使用量
- 简单的API
- 轻松地集成了许多前端和框架(从一开始就意味着作为模板引擎的后端)
缺点
- 没有其他图书馆那么快(这是有争议的,见下面的基准)
- 较less的思想和社区的使用
React团队成员SebastianMarkbåge对此发表了一些评论:
React在输出(这是一个已知的可序列化格式,DOM属性)上进行差异化。 这意味着源数据可以是任何格式。 它可以是不可变的数据结构和closures状态。
Angular模型不保留参考透明度,因此是固有的可变的。 你改变现有的模型来跟踪变化。 如果您的数据源每次都是不可变的数据或新的数据结构(比如JSON响应)呢?
脏检查和Object.observe不适用于闭包范围状态。
这两件事显然是function模式的限制。
另外,当你的模型复杂度增加时,做脏跟踪变得越来越昂贵。 但是,如果您只在可视化树上进行区分,例如React,那么它的增长速度并不会很快,因为在任何给定的点上,屏幕上显示的数据量都会受到UI的限制。 皮特上面的链接涵盖了更多的优势。
虚拟大教堂不是由反应发明的。 它是HTML DOM的一部分。 它是轻量级的,并从浏览器特定的实现细节中分离出来。
我们可以将虚拟DOM视为React的HTML DOM的本地和简化副本。 它允许React在这个抽象的世界中进行计算,并跳过“真正”的DOM操作,通常是缓慢的和特定于浏览器的。 其实DOM和虚拟DOM没有很大的区别。
以下是为什么使用Virtual Dom的要点:
当你这样做时:
document.getElementById('elementId').innerHTML = "New Value"
发生以下事情:
- 浏览器需要parsingHTML
- 它删除elementId的子元素
- 用新值更新DOM值
- 重新计算父母和孩子的CSS
- 更新布局,即每个元素在屏幕上的确切坐标
- 遍历渲染树并在浏览器上显示
重新计算CSS和更改布局使用复杂的algorithm,它们影响性能,以及更新DOM属性,即。 值。 它遵循一个algorithm。
现在,假设如果你直接更新DOM 10次,那么所有上述步骤将逐一运行,更新DOMalgorithm将需要时间来更新DOM值。
这就是为什么Real DOM比虚拟DOM慢的原因。