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