尤雨溪:Turbopack 真的比 Vite 快 10 倍吗?

4,520 阅读8分钟

导读:10 月 25 日,Vercel 推出了下一代打包工具:Turbopack,它是基于 Rust 的 Webpack 继任者,其文档中提到,Turbopack 比 Vite 快 10 倍。11 月 1 日,Vue、Vite 作者尤雨溪发表文章 《Is Turbopack really 10x Faster than Vite?》,对 Turbopack 和 Vite 进行了测试对比,下面就来看看详细内容吧!

在 Turbopack 官方文档的基准图中,最初显示带有 Turbopack 的 Next 13 能够在 0.01 秒内执行 React Hot-Module Replacement (HMR,即热更新),而对于 Vite,则需要 0.09 秒。上面也有冷启动性能基准,但由于没有一个冷启动基准显示出 10 倍的优势,我们只能假设“快 10 倍”的说法是基于 HMR 性能。

Vercel 并没有在文档中包含指向他们用来生成这些结果的基准的任何链接。所以,尤大决定使用新发布的 Next 13 和 Vite 3.2 来验证这些声明。测试的要点是通过测量以下两个时间戳之间的增量来比较 HMR 性能:

  • 修改源文件的时间,通过单独的 Node.js 进程记录文件更改;
  • 更新的 React 组件重新渲染的时间,通过 Date.now() 调用直接记录在组件的渲染输出中。 注意,此调用发生在组件的虚拟 DOM 渲染阶段,因此它不受 React 协调或实际 DOM 更新的影响。

该基准还测量了两种不同情况下的结果:

  • root:根组件,组件导入 1000 个不同的子组件并将它们一起渲染;
  • leaf:叶子组件,组件由根组件导入,但自己没有导入组件(没有子组件)。

细微差别

在测试这些之前,还有一些额外的细微差别值得一提:

  • Next 是否使用 React 服务端组件(RSC);
  • Vite 是否使用 SWC 而不是 Babel 进行 React 转换。

React 服务端组件

Next 13 引入了一个重大的架构转变,现在的组件默认为服务端组件,除非用户使用“use client”指令明确选择客户端模式。它不仅是默认设置,Next 文档还建议用户尽可能保持服务端模式,以提高最终用户的性能。

第一轮测试(Next w/ RSC 和 Vite w/ Babel))

最初的基准测试是在服务端模式下使用根组件和叶子组件测量 Next 13 的 HMR 性能。 结果表明,Next 13 在两种情况下实际上都较慢,并且对于叶子组件而言差异显著,测试方法和测试结果如下。

① 测试方法

  1. 这两个项目都是通过以下命令新创建的:
npx create-next-app@latest
npm init vite@latest  # 选择React预设
  1. 在每个项目中运行 genFiles.(m)js 生成 1000 个组件,所有组件都被导入到应用的根组件中并一起渲染。
  2. 对于每个项目,在单独的 Node 进程中运行 watch.(m)js 以获取文件更改的确切时间戳,这用于标记 HMR 的开始。
  3. 启动项目,然后编辑以下文件以测试 HMR:
  • Next: app/page.js (根组件) 和 app/comp0.jsx (叶子组件)
  • Vite: src/App.jsx (根组件) 和 src/components/comp0.jsx (叶子组件)

已编辑的组件都在其输出中包含 Date.now()。 DOM 中最终渲染的时间戳用于标记 HMR 的完成。

② 测试结果

  • 记录超过 5 次运行
  • 时间以毫秒为单位
  • 在 M1 Macbook Pro 上测试

第二轮测试(Next w/o RSC 和 Vite w/ Babel))

有人指出,应该在没有 RSC(React 服务端组件)的情况下对 Next 组件进行基准测试以使其相等。 因此,在 Next 根组件中添加了“use client”指令以选择客户端模式。事实上,在客户端模式下,Next HMR 显着改进,比 Vite 快 2 倍,测试方法和测试结果如下。

① 测试方法

  1. 这两个项目都是通过以下命令新创建的:
npx create-next-app@latest
npm init vite@latest  # 选择React预设
  1. 在每个项目中运行 genFiles.(m)js 以生成 1000 个组件。所有组件都被导入到应用的根组件中并一起渲染。
  2. 给 app/page.js 添加 use client 指令,使它以客户端模式渲染。这是确保正确的比较所必需的,因为服务端组件会产生不小的 HMR 开销(慢 4 倍)。
  3. 对于每个项目,在单独的 Node 进程中运行 watch.(m)js 以获取文件更改的确切时间戳。这用于标记 HMR 的开始。
  4. 启动项目,然后编辑以下文件以测试 HMR:
  • Next: app/page.js (根组件) 和 app/comp0.jsx (叶子组件)
  • Vite: src/App.jsx (根组件) 和 src/components/comp0.jsx (叶子组件)

② 测试结果

  • 记录超过 5 次运行
  • 时间以毫秒为单位
  • 在 M1 Macbook Pro 上测试

image.png

SWC 与 Babel 转换

我们的目标是让基准只关注 HMR 性能差异,所以需要消除另一个变量:Vite 的默认 React 预设使用 Babel 来转换 React HMR 和 JSX。

React HMR 和 JSX 转换不是与构建工具耦合的特性,它可以通过 Babel(基于 JavaScript)或 SWC(基于 Rust)来完成。Esbuild 也可以转换 JSX,但缺乏对 HMR 的支持。SWC 比 Babel 快得多(单线程时快 20 倍,多核时快 70 倍)。Vite 目前默认使用 Babel 的原因是安装大小和实用性之间的权衡。SWC 的安装量相当大(node_modules 中有 58 MB,而 Vite 只有 19 MB),并且许多用户仍然依赖 Babel 进行其他转换,因此 Babel 对他们来说是不可避免的。但是,这种情况在未来可能会发生改变。

Vite 核心并不依赖 Babel,因此使用 SWC 而不是 Babel 来处理 React 转换不需要在 Vite 中进行任何更改,只需将默认的 React 插件替换为 vite-plugin-swc-react-refresh。切换后可以看到,Vite 在根组件中有显着提升,赶上了 Next:

有趣的是,这里的增长曲线显示 Next/turbo 在根组件比叶子组件中慢 4 倍,而 Vite 只慢 2.4 倍。这意味着 Vite HMR 在更大的组件中可伸缩性更好。此外,切换到 SWC 还可以改善 Vite 在 Vercel 基准测试中的冷启动指标。

不同硬件上的性能

由于这是一个涉及 Node.js 和原生 Rust 部分的复合基准测试,因此在不同的硬件上会有很大差异。 以上结果是在 M1 MacBook Pro 上收集的。其他用户在不同的硬件上运行相同的基准测试并报告了不同的结果。在某些情况下,Vite 在根组件情况下更快,而在其他情况下,Vite 在两种情况下都明显更快。

Vercel 的澄清

在尤大发布了自己的基准后,Vercel 发布了一篇博客文章,阐明了他们的基准方法,并将他们的基准提供给公众验证。阅读文章和基准代码后,以下是一些关键点:

  • Vite 实现仍然使用默认的基于 Babel 的 React 插件;
  • 1000 个组件案例的原始数字存在舍入问题——Turbopack 的 15ms 被舍入到 0.01s,而 Vite 的 87ms 被舍入到 0.09s。 当原始数字接近 6 倍时,这进一步被宣传为 10 倍的优势;
  • Vercel 的基准测试使用更新模块的“浏览器评估时间”作为结束时间戳,而不是 React 组件重新渲染时间;
  • 文章中的图表显示,当总模块数超过 30k 时,Turbopack 可以比 Vite 快 10 倍。

总而言之,如果以下所有条件都成立,“比 Vite 快 10 倍”的说法是成立的:

  • Vite 没有使用相同的 SWC 转换;
  • 应用包含超过 30k 个模块;
  • 基准测试只测量热更新模块的评估时间,而不是实际应用更改的时间。

什么是“公平”比较?

如果我们要比较的是“开箱即用的默认设置”,那么我们应该与 Next 中启用的 RSC 进行比较,因为这是默认设置,也是 Next 积极鼓励用户使用的。 由于 Vercel 的基准测试没有使用 RSC,并且正在测量“模块评估时间”,以排除 React 的 HMR 运行时引起的差异,因此可以公平地假设基准测试的目标是对 Vite 和 Turbopack 固有的 HMR 机制进行比较。

不幸的是,在这个前提下,Vite 仍然在基准测试中使用 Babel 使这个对比是不平等的。在 Vite 中使用 SWC 进行转换更新对比结果之前,Turbopack 比 Vite 快 10 倍的结论是不准确的。

此外,我相信大多数人都会同意:

  • 对于绝大多数用户来说,30k 模块是极不可能的情况。使用 SWC 的 Vite,达到 10 倍要求所需的模块数量可能会变得更加不切实际。虽然理论上是可行的,但用它来营销 Turbopack 是不诚实的。
  • 与理论上的“模块评估”时间相比,用户更关心端到端 HMR 性能,即从保存到看到更改的时间。当看到“更新速度快 10 倍”时,普通用户会想到前者而不是后者,Vercel 在其营销中忽略了这一警告。实际上,Next 中服务端组件(默认)的端到端 HMR 比 Vite 中的慢。

作为 Vite 的作者,很高兴看到像 Vercel 这样资金雄厚的公司在改进前端工具方面进行了大量投资。在适用的情况下,甚至未来可能在 Vite 中利用 Turbopack。然而,开源工具的竞争应该建立在公开沟通、公平比较和相互尊重的基础上,令人失望和担忧的是,看到激进的营销使用了精心挑选的、未经同行评审的、边缘误导性的数字,这些数字通常只在商业竞争中出现,相信 Vercel 可以做得更好。

相关链接