使用DOM的最佳方法

在撰写Solid时,我有机会估算了优化图(基准)性能的尝试次数。

DOM是前端瓶颈开发。不同的解决方案可能导致相似的结果。每周都会出现新的图书馆,这些图书馆结合了以往的所有内容,以实现完美的组合。一段时间后,我的视线开始逃离各种解决方案。

不要误解我。我喜欢看到新的想法变成现实,但是它们都有缺陷,有时会带来便利性或生产力的损失。我理解这种决定的后果,但我想分享我的看法。

什么是使用DOM最快的方法:

  • 虚拟dom
  • 标记模板文字
  • 细粒度的可观察物

比较方式


JS Frameworks Benchmark是比较JavaScript UI框架性能的最佳开源项目。最好在本地运行测试,而不要使用官方结果。结果可能因机器而异。由于我是在性能较差的机器上进行测试,因此性能会明显下降。

我将采用最佳方法来渲染DOM树,以说明某些解决方案的冗余性。我将使用Solid支持不同渲染选项的功能来进行此操作,以便在变量更改时施加成本,并将其与其他框架的相似结果进行比较。让我们看看它们。

各种固体:

  • 固体 -版本与内置到克隆DOM节点模板更改跟踪功能顶ES2015代理二传手框架。这是通过预编译JSX模板(代码)来实现的
  • 实体信号 -此版本与前一个版本相同,但是使用原始信号代替代理。这使该库的使用复杂化,但是最终,我们得到了更小的捆绑包和更好的性能(代码)
  • 稳定点亮 -此版本在运行时不使用JSX预编译(代码)
  • solid-h-此版本使用HyperScript即时创建`document.createElement`。其余使用与Solid (代码)相同的实现

其他库:

  • domc — -, DOM DSL (domain specific language) HTML, index.html (Code)
  • surplus — JSX `document.createElement`. Solid (Code)
  • ivi — Inferno, DOM. HyperScript Helpers-esque (Code)
  • lit-html — , c . Tagged Template Literals DOM- (Code)
  • inferno是最快的React克隆,也是最快的虚拟DOM库之一。使用特殊的JSX指令以获得最佳性能(代码)

您最喜欢的一些框架可能不在这里,但是此列表显示了在最流行的框架中将看到的所有技术的优化版本。您可以将其视为评估磁带库最大功能的指标。如果您有兴趣比较流行框架的性能,建议您使用此图表。

为了进行比较,我想添加Web Assembly。不幸的是,在撰写本文时,WASM记录是没有高级抽象的原始实现。(后来他们将wasm-bindgen添加到框架中-大约是翻译器)

结果


HyperScript(Inferno,ivi,solid-h)


HyperScript将标记显示为功能的组合(例如h或React.createElement)。例如:

h('div', {id: 'my-element'}, [
  h('span', 'Hello'),
  h('span', 'John')
])

虚拟DOM框架具有此属性。即使它们使用JSX或其他DSL模板引擎-在引擎盖下,它们仍将转换为逐个元素的渲染方法。这用于为每个渲染周期构造一个虚拟DOM树。但是,如此处所示,与Solid一样,渲染的函数可用于创建反应性依赖图。



如您所见,具有虚拟DOM的库要快得多。由于过度创建反应曲线图,Solid的性能下降。注意基准测试#1,#2,#7,#8,#9的差异。



记忆力不足。 Inferno和此版本的Solid显示大致相同的结果。虽然ivi使用更多的内存。
, Solid, , VDOM. Solid , DOM, DOM. Solid JSX DOM . , . Solid fine grained evaluation . - .

反应性更新跟踪框架在更新行时显示出更好的结果。该图可以解释近年来VDOM的流行。可以说,如果您在此更新中使用HyperScript,则最好切换到虚拟DOM。

字符串模板(domc,lit-html,solid-lit)


这里的每个库都有一些共同点。它们是基于克隆元素模板呈现的,可以在运行时运行,并且不使用VDOM。但是它们仍然存在差异。 DomC和lit-html使用类似于虚拟DOM的自上而下的差异,而Solid使用反应性图。 Lit-html将模板拆分为多个部分。 DomC和Solid在运行时将模板编译为单独的路径并进行更新。



此类别具有最广泛的性能。 DomC是最快的,而lit-html是最慢的。 Solid Lit在中间。 DomC证明,简单的代码可带来最佳性能。

DomC仅在第4节中下垂,因为它计算节点的差异,随着深度的增加,该差异变得更加复杂。这非常快,但是您需要验证大数据的结果。

Solid Lit比Solid HyperScript更具生产力。运行时即时编译消除了创建反应式图形的弊端,使框架可以赶上最快的VDOM库ivi(请参阅本文末尾的完整表)。



DomC在内存消耗方面显示出良好的结果。这是由于模板元素的克隆而发生的。值得注意的是,与构建阶段的编译相比,运行时中的代码生成可以将性能开销降至最低。对于lit-html来说,这可能是不公平的比较,因为该框架未使用此技术。公平地说,lit-html或类似的库(例如hyperHTML或lighterHTML)不是实现标记模板文字的最佳方法。即使在没有VDOM的运行时,您也可以获得良好的结果。

预编译的JSX(固定,固定信号,剩余)


这些库使用JSX,在构建阶段将其编译为DOM或反应图。模板可以是任何东西,但是JSX提供了干净的语法树,可以增强开发人员的体验。



该组的结果相似,但区别非常重要。这三个都使用相同的库来管理S.js状态使用Solid Signals示例,您可以看到带有克隆模板元素的跟踪功能可提供更高的性能。使用ES2015代理会重载Solid的标准实现,这会使所有图形的结果恶化。盈余使用`document.createElement`,这会降低在创建#1,#2,#7,#8行的测试中的性能。



内存消耗也有类似的结果。在这种情况下,代理比克隆模板元素要复杂得多。

这里的结论是代理会降低性能,并且更多的库应该克隆模板。另一方面,您可以将由于代理造成的性能损失视为一项投资。在其他示例中,Solid示例的代码量最少-仅66行,它的非空白比Svelte少13%-该库以其极简主义而自豪。

一流的(domc,ivi,固态信号,vanillajs)


现在,让我们来看看每个类别的获奖者,并将它们与香草JavaScript中残酷,有效,手写的示例进行比较。每个实现都代表一种流行的状态跟踪解决方案。您甚至可以在以下三个库之间进行类比:Solid→Vue,DomC→Angular,ivi→React。如果除去渲染以外的所有多余内容,并摆脱60-200kb的代码,就会得到此结果。



在性能方面,DomC和Solid接近,ivi明显落后,但总体而言DomC更快。与vanillaJS相比,它的复杂性要低得多,但在进行部分更新时效果不佳。仅此标准不是指示性的。任何认为V​​DOM速度慢或有不必要的复杂性的人都应该自己检查一下。

大多数库永远不会具有这种性能。



DomC在内存方面也处于领先地位。在内存消耗方面,细粒度的Solid优于VDOM ivi。

有趣的是,无论使用哪种方法,这些库都不会比vanillaJS差很多。他们都非常快。

捆束尺寸


最后,我想谈谈捆绑商品的大小。许多实际测试仅关注这些指标。是的,捆绑包的大小很重要,并且与性能直接相关,但是有什么区别呢?我怀疑代码复杂度比大小更重要。



结论


像往常一样,此类图中的结果永远不会完全令人信服。过程本身和我们得出的结论很重要。在这种情况下,我们看到DOM本身在性能方面是一个很大的瓶颈。如此之多,以至于没有明确的技术来规避它。


Christoper Lambert饰演Highlander

不,不是那么简单。 DOM和VDOM都不慢。但是我相信他们彼此值得。我承认,VDOM Performance React的言论使我想到了这些想法。围绕该主题的观点的无知正在激怒。

VDOM缓慢的说法是由于意识不足。与不执行VDOM相比,渲染VDOM并计算状态差异是一个复杂的过程。但是它的缺失是否可以扩展?以及如何进行数据更改?

我看到每条规则都有一个例外。通常,在反应框架中将预编译与精细结合起来是最快的解决方案。但是,DomC显示出没有它的高性能。本地JS方法(例如使用Tagged Template Literals克隆模板元素)可能是实现大型公司(Google)的lit-html的最佳解决方案。但这是此表中最慢的框架之一,甚至不是这些技术的最佳实现。 Svelte被认为是社区中最快的图书馆,但它甚至无法与提出的解决方案竞争。

如果反应式编程获胜,这并不意味着所有反应式库都很快,或者指标意味着一切。尽管本文进行了深入的比较,但我认为实际上有快速的库和较慢的库。即使我们找到了超级技术,我们仍将面临其局限性。

一张表中所有库的测试结果:


All Articles