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

前情:

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:

复制代码
$ 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,还是可选的:

复制代码
"peerDependencies": { ..., "terser": "^5.16.0", "esbuild": "^0.27.0 || ^0.28.0", ... }

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

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

复制代码
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 计时:

复制代码
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 产物对比

再来比较体积

复制代码
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):

复制代码
$ 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 只有俩包:

复制代码
{
  '@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 数据

答案来了

复制代码
                        产物字节    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 里:

复制代码
"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 文件的薄壳。

复制代码
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」获取。

期待您的评论和建议。

相关推荐
右耳朵猫AI1 小时前
Web前端周刊2026W36 | pnpm 12 Rust 重写、Remix 3 RC、Node.js 26.8.0、htmx 4.0 大版本
前端·rust·node.js
平头哥~1 小时前
Day 21 | animation 与 keyframes:把动画从 A 到 B 变成多点编排
前端·css·css3·css学习
宿67411 小时前
vue3-包管理器
前端·vue.js
@PHARAOH11 小时前
WHAT - Remix 入门和基本实践:理解两套产物
前端·remix
mayaairi12 小时前
Vue2 生命周期完全指南:从创建到销毁的8个钩子函数
前端·javascript·vue.js
研☆香12 小时前
一个简单的气泡弹窗制作
javascript
xy345312 小时前
axure9.0 如何打造一个计时器(简单版)
前端·ui·html·axure·原型·产品设计
大模型丫丫12 小时前
用 TypeScript、NestJS 和 LangGraph 搭建一个可控的 AI Agent
javascript·人工智能·typescript
IMPYLH13 小时前
HTML 的 <pre> 元素
前端·javascript·html