Vite 8 换芯实测:Rolldown 替掉双引擎,构建快 3.19 倍

Vite 8 换芯实测:Rolldown 替掉双引擎,构建快 3.19 倍

Posted by LBX on September 8, 2026

前情:

TS 7.0(Go)

React Compiler(Rust)

Rspack 2.0(Rust)

Bun(Zig→Rust)

Vite 8 已经把 Rolldown 作为默认引擎发正式版了,是 vite build 默认走的路。两个测试项目同样的代码分别用vite7和vite8跑,看一下会有什么情况。

一、看 package.json

Vite 7 时代的架构是双引擎:开发阶段用 esbuild 做预构建(transform),生产阶段用 Rollup 打包。

两套引擎,两套插件体系,一次构建要过两道手。

Vite 8 干的事,就是把这个双引擎拆了,换成 Rolldown 一个。看看npm 上 vite@8.2.2 的 dependencies:

1
2
3
4
5
6
7
8
$ npm view vite@8.2.2 dependencies
{
  "postcss": "^8.5.26",
  "rolldown": "~1.2.4",        -- 引擎换人了
  "picomatch": "^4.0.5",
  "tinyglobby": "^0.2.17",
  "lightningcss": "^1.33.0"    -- CSS 处理也换成 Rust 系
}

esbuild 的位置从 dependencies 退到了 peerDependencies,还是可选的:

1
"peerDependencies": { ..., "terser": "^5.16.0", "esbuild": "^0.27.0 || ^0.28.0", ... }

一个 dependencies、一个 peerDependencies,引擎的话语权变化写在 manifest 里。npm view 出来的依赖树才是真实的。

Rolldown 的时间线看一下,走的还是很快的

1
2
3
4
5
2025-12-03  Vite 8 Beta(rolldown-vite 试验包转正前奏)
2026-03-12  Vite 8.0 正式发布:Rolldown 成为默认且唯一的打包引擎
2026-05-07  Rolldown 1.0 发布(官方口径:比 Rollup 快 10-30 倍,与 esbuild 打平)
2026-06-23  Vite 8.1
  至今      vite 8.2.2 + rolldown 1.2.7,8 月还在活跃迭代

Vite 7 时代有个 rolldown-vite 包(npm 上停在 7.3.1),就是官方提前放出来的换芯预览版,大量兼容性验证是在背地里跑完的。

Vite 官方中文文档现在也还留着从 v7 迁移的指引,把 rolldown-vite 列为迁移到 Vite 8 的中间步骤。

二、双引擎痛点

1. 同一份代码,transform 和 bundle 各解析一遍。 测试时 esbuild 把你的代码过一遍,生产时 Rollup 再从头解析一遍。两套解析器的行为差异(比如不同的警告口径、不同的 ESM 处理细节)也就是意味着测试好好的,生产出问题会一直存在。

2. 插件双轨制。 esbuild 插件和 Rollup 插件是两套接口,Vite 插件作者经常要写兼容分支,社区里也有很多Vite 插件不支持的 issue。

Rolldown 统一引擎后,上述两个问题自然消失:transform 和 bundle 共用同一套解析器,插件只需面向 Rollup 兼容层写一套。

三、同样的代码分别测试

3.1 测试项目

两个目录,分别装 vite@7.3.6 和 vite@8.2.2,代码部分都是一样的:

  • 30 个业务模块,每个模块真实 import lodash-es 的 camelCase 和 date-fns 的 addDays
  • 每个模块埋一个永远不被引用的导出(unusedN = 'dead-code-N'),专门测 tree-shaking
  • 入口汇总 30 个函数跑一遍

纯 vanilla,不带框架插件——把插件变量锁死,只测引擎差异。

3.2 构建耗时:3.19 倍

各跑 5 轮,用 Python 的 perf_counter 计时:

1
2
vite7(Rollup+esbuild): [807.8, 795.2, 799.4, 827.9, 906.5] | 均值 827.4ms
vite8(Rolldown):       [259.8, 259.6, 262.8, 255.6, 259.4] | 均值 259.4ms

827.4 → 259.4,3.19 倍

还有就是稳定性,vite7 的五轮里有一轮飙到 906ms(JS 引擎的 GC 抖动),vite7 五轮标准差约 43.5ms,vite8 只有 2.7ms——Rust 侧不仅快,抖动也小了一个数量级。

3.3 产物对比

再来比较体积

1
2
vite7: dist/assets/index-Dpyeg0G3.js  9.78 kB │ gzip: 3.70 kB   (9588 字节)
vite8: dist/assets/index-x0SZjdEd.js  9.55 kB │ gzip: 3.63 kB   (9360 字节)

体积差 228 字节,rolldown 略小。两边的 tree-shaking 都很干净——我 grep 了产物,「dead-code-1」这个字符串两侧都不存在,30 个死导出全部剔除。这一点上 Rollup 多年积累的摇树能力没有被 Rolldown 输掉,兼容性担忧可以先放一放。

模块数统计 977(vite7)vs 978(vite8),差 1 个。

这个倒是没有去深究,大概率是两版对依赖 ESM 解析粒度的细微差异,不影响产物正确性。可以作为提醒,换引擎后产物 hash 全变,CDN 缓存策略该检查了。

3.4 单独使用

Rolldown 也能脱离 Vite 单独用,我拿它直接打包入口(带内置 minify):

1
2
3
4
5
$ rolldown src/main.js -f esm -o out-cli.js -m
三轮耗时: [125.2, 126.6, 121.3] ms | exit=0
产物字节: 8658 | 行数: 1 | 含 dead-code-1? False
=== 功能验证:node out-cli.js(minify 后产物直接执行)===
mod1Key:1:2026|mod2Key:4:2026|mod3Key:9:2026|mod4Key:16:2026|mod5Key:25:2026|mod

功能验证的输出是mod1Key:1:2026 ,完全符合源码逻辑(camelCase(‘mod_1_key’) + 1×1 + 年份),但产物里 grep 不到 camelCase 这个名字了——minify 把标识符 mangling 成了短名。所以验产物别 grep 函数名,跑一遍看行为。

需要注意的是rolldown 的 CLI 不认 rollup 的 -i 参数,input 是位置参数(rolldown src/main.js)。API 兼容 Rollup,CLI 不完全兼容,查一眼 --help 能省五分钟。

3.5 30 倍的水分在哪

官方口径是10-30 倍是对比 Rollup 单体,对比 esbuild 的说法是「on par」。官方仓库的三方 benchmark 里,rolldown 1032ms vs esbuild 1137ms——基本打平,甚至略快。

至于测试项目里为什么是 3 倍多?那是整条 Vite build 管线的对比,不是裸 bundler 的对比。

Vite 7 的双引擎要在 Rollup 之前先跑一轮 esbuild 预构建,管线开销天然比单引擎大;Vite 8 单引擎一次过,省的就是这部分结构性损耗。

引擎层 Rolldown 与 esbuild 同级,但单引擎架构让 Vite 整体甩掉了双引擎的结构性开销。快 30 倍的标题党数字看看就好,你的项目升级后能快多少,取决于你管线里双引擎损耗占比多大——我这个小项目是 3 倍多,你的中型项目大概率也是这个量级,超大型项目值得自己测。

四、换了 minifier ,同进程测试

3.4 节里 rolldown CLI 带 -m 出的产物,用的就是内置的 Oxc minifier。

这个 oxc 到底啥呢?快在哪儿?

npm view rolldown@1.2.7 dependencies 只有俩包:

1
2
3
4
{
  '@oxc-project/types': '=0.148.0',   ← oxc 的类型包,精确版本锁死(无 ^ 无 ~)
  '@rolldown/pluginutils': '^1.0.0'
}

注意那个 =0.148.0——不带 ^,精确锁定。

oxc 需要交代一下:它是 VoidZero 生态里跟 Rolldown 并排的另一根支柱(Oxidation Compiler),解析器、转换器、minifier 一套 Rust 工具箱。Rolldown 直接编进自己的引擎,版本跟着自己走;oxc 自己也独立发 npm 包(@oxc-minify/binding-linux-x64-gnu 已到 0.149.0),各走各的发版节奏。所以 rolldown 1.2.7 锁 0.148.0、oxc 独立包已经 0.149.0,两边号不同步——正常,关系都写在 manifest 里。

Vite 7 时代 minify 是 esbuild 的活(terser 可选),Vite 8 换成 oxc

4.1 同一个进程,API 直接调

比 minifier 有个大坑:进程启动时间会污染数据(terser 是纯 JS,光 node 启动就上百毫秒)。所以不用 CLI,把三个 minifier 拉进同一个 node 进程,API 级直调:

  • oxc:rolldown 的 napi binding 里直接曝着 minifySync 函数(那个 19MB 的 .node 二进制,细节在第五个板块),进程内调 Rust
  • esbuild:vite 7.3.6 自带的 0.28.2(Go),esbuild.transform(code, { minify: true })——新版没有顶层 minify 了,transform 加 minify 参数是等效写法
  • terser:5.51.2(纯 JS 老牌基准),terser.minify(code, { compress: true, mangle: true })

输入统一用 rolldown 打包的未压缩产物(35,605 字节,30 模块 + lodash-es/date-fns),预热 3 轮、计时 20 轮取均值,gzip 用 level 9。

三个里面谁压得最小、谁最快

4.2 数据

答案来了

1
2
3
4
                        产物字节    gzip(9)   耗时均值(20轮)
oxc 0.148 (Rust)        11,869      3,763      0.59 ms
esbuild 0.28.2 (Go)     12,733      3,704      2.22 ms
terser 5.51.2 (JS)       8,083      2,971     35.10 ms

三个产物都用node 跑了一遍,输出逐字一致(mod1Key:1:2026|mod2Key:4:2026|...)——压缩不是比谁改得狠,行为不能变。

速度:oxc 0.59ms,比 esbuild和terser都快。oxc 和 esbuild 都是原生二进制(Rust vs Go),esbuild 已经很快了,oxc 还要更快。terser 的劣势主要在 watch 场景下的持续开销。

压缩率:terser 压得最小,oxc 和 esbuild 基本持平。原始字节 oxc 比 esbuild 小,但 gzip 之后 esbuild 反而小。说明两家 minify 的方式不同:oxc 把标识符压得更狠,esbuild 的产物里重复模式更多、对 gzip 更友好。

所以 Vite 8 选 oxc 的逻辑很清楚:dev 和 build 要的是速度,压缩率上 oxc 没输,压缩率最好但慢的 terser 就需要自己来配置了。

五、19MB 的 .node

rolldown 的 npm 包里,一堆 .mjs 壳子,真引擎全在 optionalDependencies 里:

1
2
3
4
5
6
"optionalDependencies": {
  "@rolldown/binding-darwin-x64": "1.2.7",
  "@rolldown/binding-linux-x64-gnu": "1.2.7",
  "@rolldown/binding-win32-x64-msvc": "1.2.7",
  ...
}

npm 只装当前平台那一个,里面的 rolldown-binding.linux-x64-gnu.node——19MB 的原生二进制,napi-rs 的编译产物。这就是 4.1 节能进程内直接调用minifySync 的原因:JS 层只是个 require 那个 .node 文件的薄壳。

1
2
minify, minifySync, parse, parseSync, transform, transformSync,
enhancedTransform, isolatedDeclaration, resolveTsconfig, ...

parse、transform、minify 全是原生函数,oxc minifier 不是外部依赖,是直接编译进这个 .node 的 Rust 代码。

vite8 五轮 260ms 左右的稳定性、oxc 0.59ms 的单轮耗时,构建全链路在原生代码里跑完,node 进程只是个宿主。

这意味着 Rolldown 的构建全链路(解析、转换、压缩)都在原生代码里跑完,Node 进程只是个宿主。这是它与纯 JS 打包器(如 Rollup)最根本的差异:CPU 密集的工作彻底离开了 JS 的解释执行路径

六、迁移成本很低,但是有几点需要注意

官方给 Vite 7 用户的建议路径:小项目直接 npm i vite@8;大项目先切 rolldown-vite(Vite 7 + Rolldown 引擎的过渡包)跑稳,再升 Vite 8。

实测项目的话,是直接切过去的,没有做任何配置改动,零兼容问题,但那是因为纯 vanilla。你的真实项目就不一样了:

  1. CSS:Vite 8 用 lightningcss 替代了部分 PostCSS 处理,跑一遍 npm run build 检查样式是否正常
  2. Rollup 插件:检查是否用了 generateBundlerenderChunk 等钩子,关注 Vite 8 的插件兼容性 changelog
  3. 产物 hashnpm run build 后对比产物文件名,更新 CDN 缓存策略

总结

Rolldown 不是又一个更快的打包器,而是消除esbuild 和 Rollup 的双引擎内耗、插件双轨制、dev/build 行为不一致等等。

每一次换地基,不仅仅只有快。

Rolldown 和 oxc 背后的 VoidZero(尤雨溪的公司),一整条工具链,下篇可以研究一下这张棋盘:Vite、Rolldown、Oxc、Vitest 如何互相咬合,和 Vercel 的 Turbopack 又是什么对位关系。

参考

  • Vite 8.0 is out!(vite.dev blog,2026-03-12)
  • Rolldown 1.0(voidzero.dev,2026-05-07)
  • rolldown-rs/rolldown GitHub 仓库与官方 benchmark
  • Vite 官方中文文档:从 v7 迁移
  • npm registry:vite@8.2.2 / rolldown@1.2.7 / rolldown-vite@7.3.1 / @oxc-project/types@0.148.0 / @oxc-minify/binding-linux-x64-gnu@0.149.0

测试的demo可以在gzh程序员蜡笔熊回复「vite8」获取。

期待您的评论和建议。