重新审视 UI 组件的真正成本

2025-06-10

重新审视 UI 组件的真正成本

最近我的工作重心又回到了 Solid 1.0 版本的优化上,所以我想重温一下我之前写的那篇《UI 组件的真实成本》文章。最初写这篇文章的时候,我并不确定会发现什么,而且我当时也有点小心翼翼,不想冒犯任何人。我让每个框架都以 Level 0 为基准进行展示,然后就以此为基础进行构建。

不均衡实现的缺点是,我实际上没有展示虚拟 DOM 的权衡,而且我完全忽略了 Web 组件的开销。所以我想带着这个问题重新审视一下。

为什么是现在?我最近一直在对Stencil和新的Lit进行基准测试。由于它们都不支持原生内置函数,这让我有点烦。这是一个问题,因为基准测试使用 LitHTMLTableElements意味着它们不能随意插入自定义元素。所以这些实现都是在一个大型组件中完成的。我想看看能否更好地模拟它们的扩展方式。

强制性免责声明:我编写了 Solid,但并未创建此基准测试。请接受它。我希望您能从中学到的不仅仅是 Solid 的速度。不同的技术扩展性有所不同,这才是重点所在。

设置

这次测试再次采用了JS Frameworks Benchmark的修改版。这是我们 TodoMVC 应用的升级版。它会用一些夸张的数据来测试我们的实现,但我们很快就能发现任何瓶颈。

需要注意的是,考虑到原生内置函数的限制,我们将使用手动优化的 Web Component 解决方案。这意味着性能会比 Lit 通常表现更好。所以情况略微偏向 Lit,但这是我能做到的最好的了。

我最初是在新款 M1 Macbook Air 上进行的测试,但考虑到 CPU 节流(已知问题)的问题,我也在 Intel i7 Macbook Pro 上进行了测试。这会让测试结果略显混乱,但有助于我们了解在最新、最强大的设备上运行和在较慢的设备(通过 CPU 节流)上运行的区别。

场景

  • 级别 1:整个基准测试在单个组件中实现。
  • 第 2 级:每个行和每个按钮都制作一个组件。
  • 第三级:每行进一步细分为四个表格列的单元格组件,删除图标也制成一个组件。

竞争者

1. Inferno目前最快的虚拟 DOM 库之一。虽然与 React 有所不同,但它与 React 兼容,并将在本次测试中作为 VDOM 库的替代。来源 [ 1 , 2 , 3 ]

2. Lit Google 支持的 Tagged Template 渲染库。由于缺乏对原生内置函数的支持,我使用了优化的手写自定义元素包装器。我还保留了显式事件委托,这比其他所有非原生实现都更具优势。来源 [ 1 , 2 , 3 ]

3. Solid最快的运行时响应式库。它的组件其实就是工厂函数,所以应该可以作为一个很好的比较对象。来源 [ 1 , 2 , 3 ]

4. Svelte巧妙利用编译器生成最小的 bundles。它也有自己的组件系统。来源 [ 1 , 2 , 3 ]

5. vanillajs:并非框架,只是核心实现。我采用标准实现并在升级过程中逐渐添加 Web 组件[ 1、2、3 ]

基准测试

我认为与其一次只关注一个框架,不如从层次上来看待这个问题。相对定位更能体现趋势。尽管随着我们添加更多组件,库的速度会因差异而变慢,但我们的基准是通过使用 Vanilla JS 和 Web 组件来与我们一起发展。

我们将大量使用平均几何平均值(底部一行)来全面比较这些库。查看单个结果以获取更多信息固然重要,但这也为我们提供了一种确定相对排名的简单方法。

级别 1 - 一体化

您只能获得一个组件/应用。虽然对于大多数库来说,这是最优版本,但对于 VDOM 来说并非如此,因为组件对于管理更新性能至关重要。

M1
1 级 - M1

英特尔(速度较慢)
1级-英特尔

这可能是你见过 Inferno 表现最差的一次,但这不是它的错。如果每个人都按照 Rich Harris 的《虚拟 DOM 纯粹是开销》一书中描述的方式编写 VDOM 代码,就会出现这种情况。希望大多数人不要这样做。实际上,在大多数情况下,这还不算太糟糕,但在选择基准测试和更新较为不完整的情况下,确实会受到很大影响。

第 2 级 - 行和按钮

就组件分解而言,我认为这是许多框架相当典型的情况。VDOM 现在有足够的组件可以运行。

M1
2 级 - M1

英特尔(速度较慢)
2级-英特尔

由于 Vanilla 添加了 Web 组件,它与 Solid 之间的差距已经消失。Inferno 现在拥有足够的组件,速度显著提升。Lit、Svelte 和 Vanilla 之间的差距正在缩小。因此,它们的组件成本看起来相当。

第 3 级 - Components `R Us

在这个层级上,每个表格单元格都是一个组件。这种划分对某些人来说可能有点极端。在虚拟 DOM 领域,我们已经习惯了这种包装方式。像 Styled Components 和 Icon 库这样的工具让我们毫不费力地就采用了这种模式。那么,这到底有多贵呢?

M1
3级-M1

英特尔(速度较慢)
第 3 级 - 英特尔

在我们优化的 Vanilla JS 中添加 Web 组件实际上使其比等效的 Solid 示例更加昂贵。Inferno 现在已显著缩小了与 Vanilla JS 的差距。而 Svelte 和 Lit 则继续下降了一些分数。在速度较慢的系统上,Svelte 目前在诸如清除行等基准测试中,其内存占用确实受到了损害:

英特尔速度放缓
3 级内存 - 英特尔

结论

我感觉自己像在重复老生常谈,但我们真的不应该把 Web 组件和 JavaScript 框架组件进行比较。它们的用途不同,性能也不是它们能够取胜的地方。只要你明白它们不是一回事,就没什么问题了。

这次测试的设置对 Web Components 来说确实有利。没有 Shadow DOM 或插入额外元素。这些在现实世界中常见的问题会让它们成为更重的解决方案。我不希望出现任何冲突,所以保留了诸如显式事件委托之类的功能,这在这次测试中只会对 Lit 有利。这确实是对 Web Components 最乐观的看法。

情况可能并非总是如此。自从我上次测试以来,Web Component 的性能在两年内有所提升。但这并非仅仅说“使用平台”那么简单。事实证明,所有 JavaScript 框架都使用该平台,只是有些框架比其他框架更高效。这需要在“为了标准而使用平台”和“仅在经验上有益的情况下使用平台”之间取得微妙的平衡。这里涉及的因素远不止性能。

但很明显,能够很好地扩展更多组件的框架(例如 React 或 Inferno 等虚拟 DOM 库或Solid 等“无组件”库)不会产生那么多的开销。

这次对我来说,这算不上什么启示。但或许通过观察一些数字,我们可以更好地推断出应该谨慎的地方。这只是一个残酷的微基准测试,它实际上只展示了框架级别的瓶颈,真正的瓶颈通常发生在用户代码中。但对于那些希望从纯技术角度进行评估的人来说,这或许还是有一定价值的。


单个表格中的结果英特尔(速度较慢)

完整表格 - 英特尔

鏂囩珷鏉簮锛�https://dev.to/this-is-learning/the-real-cost-of-ui-components-revisited-4d23
PREV
利用信号进行本地思考
NEXT
Typescript 4.5 中模板字符串类型作为判别式