Vue:我们将成为最快的响应式框架!

科技   2024-11-25 16:16   上海  

大家好,我是 ssh


太长不看版:

在 JavaScript 框架竞争中,Vue.js 团队宣布其响应式系统将成为最快的。通过引入 alien-signals 库,Vue 3.5 实现了显著的性能提升,特别是在内存使用和计算效率方面。新的实现降低了内存使用量,提升了性能,并优化了代码抽象,使得 Vue 在各种性能测试中表现出色。

学习要点

  1. Vue.js 性能提升:Vue 3.5 引入 alien-signals 库,显著提升了响应式系统的性能。
  2. 内存优化:内存使用量减少约 13%,从 2.3MB 降低到 2.0MB。
  3. 性能测试结果:在多种性能测试中,Vue 3.5 表现优异,性能提升超过 30 倍。
  4. 代码抽象改进:新的实现消除了与外部实现的耦合,使代码更简洁和易于维护。
  5. 具体优化措施:包括移除命名空间使用,提升模块根级导出的性能。
  6. Alien Signals 细节alien-signals 库通过严格的约束条件实现卓越性能,整体性能约为 Vue 3.4 的 400%。
  7. 示例代码:提供了基本用法和 effect 作用域的示例,展示了 alien-signals 的实际应用。
  8. 对比分析:JS Reactivity Benchmark 显示 alien-signals 在性能上领先其他响应式框架。

以后的文章我都会加入这个部分,基于 AI 总结,方便没空了解细节的同学快速阅读。


正文如下:

在 JavaScript 框架竞争激烈的今天,提高性能始终是各大框架追逐的目标。最近,Vue.js 团队在 X 上高调宣布,Vue 的响应式将成为所有框架中最快的。

并附上了一个重要的 GitHub PR 链接:升级PR。

https://github.com/vuejs/core/pull/12349

这个 PR 的核心是引入了 alien-signals,一个基于 Vue 3.4 响应式系统重新编写的 signals 库,旨在确保响应式系统的高性能实现,目前,这是所有 signals 库中最快的实现。

在我之前的文章里,详细介绍过 Signals 的标准化提案,感兴趣可以回顾一下:

主要优化点


  1. 降低内存使用量

在创建大量 refcomputedeffect 实例时,内存使用减少了约 13%(从 2.3MB 降低到 2.0MB)。


  1. 提升性能

各种性能测试结果更优,尤其是在更改 ref 后读取大量 computed 的场景。Vue 3.5 在这种情况下存在显著的性能缺陷,而当前实现解决了这个问题,性能提升超过 30 倍(随规模增加而提升)。


  1. 更好的代码抽象

以前的调度逻辑与外部实现存在耦合(如 Dep 清理、调试事件、递归处理);新的实现消除了这些耦合,使代码更简洁和易于维护。

性能提升对比

以下是基准测试的结果:

     name                                                                    hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · create computed                                               6,111,709.97  0.0000  0.3307  0.0002  0.0002  0.0002  0.0002  0.0003  ±0.53%  3055856  [1.28x] ⇑
     create computed                                               4,790,560.55  0.0001  2.8602  0.0002  0.0002  0.0003  0.0003  0.0005  ±1.19%  2395281  (baseline)
   · write ref, don't read computed (without effect)               3,725,946.87  0.0002  0.1777  0.0003  0.0003  0.0003  0.0003  0.0004  ±0.16%  1862974  [1.19x] ⇑
     write ref, don'
read computed (without effect)               3,138,437.74  0.0002  1.4873  0.0003  0.0003  0.0004  0.0004  0.0005  ±1.21%  1569219  (baseline)
   · write ref, don't read computed (with effect)                  1,629,737.97  0.0005  0.1867  0.0006  0.0006  0.0007  0.0007  0.0008  ±0.11%   814869  [1.20x] ⇑
     write ref, don'
read computed (with effect)                  1,362,345.64  0.0006  0.1455  0.0007  0.0008  0.0008  0.0008  0.0010  ±0.13%   681173  (baseline)
   · write ref, read computed (without effect)                     1,961,824.76  0.0004  0.1808  0.0005  0.0005  0.0005  0.0006  0.0007  ±0.12%   980913  [1.06x] ⇑
     write ref, read computed (without effect)                     1,848,260.85  0.0004  0.1488  0.0005  0.0005  0.0006  0.0006  0.0007  ±0.13%   924131  (baseline)
   · write ref, read computed (with effect)                        1,475,801.43  0.0005  0.2211  0.0007  0.0007  0.0008  0.0008  0.0010  ±0.12%   737901  [1.14x] ⇑
     write ref, read computed (with effect)                        1,296,434.21  0.0007  0.2508  0.0008  0.0008  0.0008  0.0009  0.0011  ±0.16%   648218  (baseline)
   · write ref, don't read 1000 computeds (without effect)         3,300,382.44  0.0002  0.1930  0.0003  0.0003  0.0003  0.0004  0.0004  ±0.14%  1650192  [1.03x] ⇑
     write ref, don'
read 1000 computeds (without effect)         3,189,502.88  0.0002  0.1763  0.0003  0.0003  0.0003  0.0004  0.0005  ±0.14%  1594752  (baseline)
   · write ref, don't read 1000 computeds (with multiple effects)      3,222.67  0.3063  0.5532  0.3103  0.3108  0.3488  0.3917  0.5250  ±0.19%     1612  [1.38x] ⇑
     write ref, don'
read 1000 computeds (with multiple effects)      2,329.65  0.4206  0.6286  0.4292  0.4292  0.4997  0.5215  0.5671  ±0.19%     1165  (baseline)
   · write ref, don't read 1000 computeds (with single effect)         3,734.95  0.2648  0.3821  0.2677  0.2683  0.2855  0.2986  0.3510  ±0.09%     1868  [1.71x] ⇑
     write ref, don'
read 1000 computeds (with single effect)         2,186.19  0.4434  0.6528  0.4574  0.4565  0.5061  0.5221  0.5846  ±0.16%     1094  (baseline)
   · write ref, read 1000 computeds (no effect)                        4,694.97  0.2105  0.3849  0.2130  0.2142  0.2301  0.2382  0.3271  ±0.12%     2348  [1.26x] ⇑
     write ref, read 1000 computeds (no effect)                        3,718.71  0.2292  9.4632  0.2689  0.2329  1.0790  2.5163  6.3315  ±5.98%     1860  (baseline)
   · write ref, read 1000 computeds (with multiple effects)            2,845.64  0.3453  0.6213  0.3514  0.3512  0.4014  0.4358  0.5546  ±0.21%     1423  [1.33x] ⇑
     write ref, read 1000 computeds (with multiple effects)            2,142.70  0.4569  0.7351  0.4667  0.4664  0.5183  0.5565  0.6951  ±0.22%     1072  (baseline)
   · write ref, read 1000 computeds (with single effect)               2,265.62  0.4338  0.9382  0.4414  0.4392  0.5991  0.6087  0.6304  ±0.36%     1133  [1.48x] ⇑
     write ref, read 1000 computeds (with single effect)               1,529.78  0.6415  0.8937  0.6537  0.6526  0.7195  0.7378  0.8937  ±0.18%      765  (baseline)
   · 1000 refs, read 1 computed (without effect)                      19,686.23  0.0490  0.1665  0.0508  0.0495  0.0595  0.0631  0.0935  ±0.16%     9844  [3.41x] ⇑
     1000 refs, read 1 computed (without effect)                       5,775.97  0.1611  0.3445  0.1731  0.1746  0.2057  0.2170  0.2689  ±0.18%     2888  (baseline)
   · 1000 refs, read 1 computed (with effect)                         22,764.26  0.0427  0.1713  0.0439  0.0430  0.0534  0.0585  0.0983  ±0.16%    11383  [3.63x] ⇑
     1000 refs, read 1 computed (with effect)                          6,268.22  0.1492  0.3157  0.1595  0.1620  0.1907  0.2024  0.2408  ±0.18%     3135  (baseline)
  • 创建 computed

    • 新版本:每秒 6,111,709.97 次,[1.28x] 提升
    • 基准版本:每秒 4,790,560.55 次
  • 修改 ref,不读取 computed(无 effect

    • 新版本:每秒 3,725,946.87 次,[1.19x] 提升
    • 基准版本:每秒 3,138,437.74 次
  • 修改 ref,读取 1000 个 computed

    • effect 场景下,新版本提升 1.26 倍
    • effect 场景下,新版本提升 1.33 倍

详细的基准测试请参考 GitHub PR 链接中的表格(https://github.com/vuejs/core/pull/12349)。

随后,尤大也补充了一个 commit,移除命名空间的使用:

  • 原因:之前使用命名空间是因为在基准测试中表现出更好的性能,但发现这是由于 Vitest 基准测试目前的模块评估方式造成的。跨模块导入绑定的每次访问都有开销,从而影响了性能。
  • 解决方案:将模块根级导出提升到本地常量中,以此来代替命名空间访问。这种方法不仅可以提升性能,还更适合代码压缩器,并能减少捆绑包的大小。
  • 结果:使用捆绑反应模块进行基准测试后,发现删除命名空间方式后性能略有提升,对比 571ba05 之前和之后的结果,整体改进显著。

体积报告

此次优化对包大小的影响:

文件大小变化Gzip 变化Brotli 变化
runtime-dom.global.prod.js+1.11 kB+121 B+98 B
vue.global.prod.js+1.11 kB+114 B+71 B

Alien Signals 项目细节

项目简介

alien-signals 是一个轻量级的信号库,由 StackBlitz 推出,目标是创建一个具有最低开销的 signal 库。

Github 地址:https://github.com/stackblitz/alien-signals

这个库旨在通过一些限制条件来实现卓越的性能:

  • 基于 Push-Pull 模型
  • 不使用动态对象
  • 不使用 Array/Set/Map
  • 不进行递归调用
  • 类属性少于 10 个

主要特点

实验结果表明,使用这些约束条件,可以在不使用复杂调度策略的情况下实现信号库的卓越性能。alien-signals 的整体性能约为 Vue 3.4 响应式系统的 400%。

Vue.js 的提升

经过优化后,Vue 的主要功能需求已经完成,并且性能保持在 Vue 3.4 的 400% 左右,而在 Vue 3.5 的某些情况下可以达到原来的 6500%。

示例代码

基本用法:

import { signal, computed, effect } from 'alien-signals';

const count = signal(1);
const doubleCount = computed(() => count.get() * 2);

effect(() => {
  console.log(`Count is: ${count.get()}`);
}); // Console: Count is: 1

console.log(doubleCount.get()); // 2

count.set(2); // Console: Count is: 2

console.log(doubleCount.get()); // 4

Effect 作用域:

import { signal, effectScope } from 'alien-signals';

const count = signal(1);
const scope = effectScope();

scope.run(() => {
  effect(() => {
    console.log(`Count in scope: ${count.get()}`);
  }); // Console: Count in scope: 1

  count.set(2); // Console: Count in scope: 2
});

scope.stop();

count.set(3); // No console output

对比其他框架

以下测试结果基于:JS Reactivity Benchmark(https://github.com/milomg/js-reactivity-benchmark)

JS Reactivity Benchmark 是一个旨在为各类响应式框架进行性能基准测试的项目。该项目通过配置依赖图形的形状、密度和读取率,提供了高可调整性的基准测试环境,允许开发者方便地添加新的基准测试和框架。

最后

参考:

  • https://github.com/vuejs/core/pull/12349
  • https://github.com/milomg/js-reactivity-benchmark
  • 点赞在看是最大的支持⬇️❤️⬇️

前端从进阶到入院
我是 ssh,只想用最简单的方式把原理讲明白。wx:sshsunlight,分享前端的前沿趋势和一些有趣的事情。
 最新文章