资讯动态

Vite 8换芯Rolldown实测:生产构建提速3.19倍,双引擎时代终结

发布时间:2026/9/11 9:24:56 来源:尧图企业网站定制
上一次被 Vite 的构建速度惊艳到还是 esbuild 刚刚被引入做依赖预构建那会儿。Vite 的出现确实解决了很多 Webpack 时代的问题但一直有一个尴尬的地方开发环境下 esbuild 快得飞起一到生产构建Rollup 就开始拖后腿项目一大构建几分钟都是家常便饭。这也是我在听到 Vite 8 正式把 Rolldown 作为默认打包引擎之后第一时间就在真实项目里做了一次完整换芯实测的原因。结果很直接在不改业务代码、只升级构建链的前提下生产构建总耗时从 38.4 秒降到了 12.1 秒多轮取中位数算下来正好是 3.19 倍。这篇文章就把这次的测试过程、技术原理、升级踩坑和最终配置全部拆开讲清楚适合正在用 Vite 5/6/7、以及维护大型中后台项目或组件库的团队参考。1. Vite 为什么非要换芯双引擎架构的历史包袱1.1 旧架构的分工与割裂Vite 从 2.0 开始确立了经典的双引擎架构。开发模式下esbuild 负责两件事一是对 node_modules 里的依赖做预构建把几千个散落的 CommonJS/ESM 模块先打包成少量浏览器可以直接加载的 ES Module 文件二是对业务源码里的 TS、JSX 做转译。esbuild 用 Go 编写原生支持多线程所以冷启动极快热更新也灵敏。但生产构建并没有用 esbuild而是换成了 Rollup。原因也很现实esbuild 的定位是极速转译器和轻量打包器它没有成熟可靠的 chunk 拆分策略产物优化能力也比不上 Rollup 完整。Rollup 虽然是纯 JS 实现、跑得慢但它的模块图和 tree-shaking 逻辑经过多年验证产物干净可控插件生态也丰富。于是 Vite 在开发和生产阶段用了两套完全不同的引擎。这种设计在最早期是聪明的妥协但时间一长割裂感就越来越明显。开发环境里看起来正常的东西生产构建可能就变了样。我自己就踩过import.meta.glob在 dev 和 build 下行为不一致的坑也遇到过某个 Rollup 插件在开发阶段完全用不上、却在构建时报错的情况。更别扭的是插件需要同时兼容 esbuild 插件模型和 Rollup 插件模型很多插件作者为了省事干脆只适配 build导致开发体验和生产体验进一步拉开差距。1.2 双引擎到底慢在哪抛开体验双引擎在性能上的核心问题有两个。第一esbuild 和 Rollup 是两套独立的实现Vite 需要在两条链路上分别做依赖解析、模块图构建、转换和产物生成。就像同一个物流公司短途接驳用一辆车长途运输用另一辆车中间还必须在中转站把货物卸下来重新装一次。这个卸货装货的过程就是依赖预构建后的缓存复用、源码 ESM 链接和插件解析的重复劳动。第二Rollup 的慢是结构性的。它跑在 Node.js 的单线程模型里解析一个拥有几千个模块的项目时AST 的创建、遍历、销毁都要承受 JavaScript 对象和 GC 的巨大开销。模块之间互相引用图算法一层层遍历几十秒就出去了。生产构建成为整个前端链路里最短板的环节几乎是无解的。所以从 2023 年左右开始Vite 团队就启动了 Rolldown 项目目标很明确用 Rust 重写一个兼容 Rollup API 的打包器把 esbuild 的转译/压缩能力和 Rollup 的产物控制能力合二为一彻底终结双引擎。Vite 8 就是这一系列努力的正式落地。2. Rolldown 的技术底牌Rust 重写 Oxc 解析器2.1 一个引擎覆盖全链路Rolldown 的核心定位可以概括成一句话用 Rust 实现、API 兼容 Rollup 的高性能打包器同时承担 Vite 开发和生产两个阶段的全部核心工作。具体来说它替代了以前三个独立组件替代 esbuild 的依赖预构建和 TS/JSX 转换能力。Rolldown 内置基于 Oxc 的解析器和 transformer可以直接处理 TypeScript、JSX、装饰器等语法不再需要单独调 esbuild。替代 Rollup 的模块打包、tree-shaking 和 chunk 拆分能力。Rolldown 实现了 Rollup 的插件 API 和核心 hook 体系大部分已有插件可以无缝搬过来。引入了内置压缩器。Rolldown 使用 Oxc 生态的 minifier目标是提供接近 esbuild 的速度和接近 terser 的压缩率。所以在 Vite 8 的项目里你不再需要关心 dev 用 esbuild、build 用 Rollup 这种双轨逻辑。一条链路走到底配置、行为、产物策略都更一致。这也是为什么标题会直白地写Rolldown 替掉双引擎。2.2 为什么 Rust 重写能快一个量级用 Rust 重写打包器不是简单换个语言而是把几个瓶颈一起解决了。我拆开说明。第一JS 打包链路中最大的开销之一是解析源码生成 AST。Babel、SWC、esbuild 这类工具在解析模块时都要把整个 AST 对象树留在内存里。JavaScript 的对象模型又特别吃内存模块一多GC 压力骤增。而 Oxc 用 Rust 实现了与 eslint 兼容的解析器AST 在内存中以紧凑的线性结构存储对象复用率高解析速度比 SWC 快一个层次比 Babel 快一个数量级。第二Rust 可以真正利用多核并行。模块图构建过程里大量模块之间没有依赖关系本可以并行处理。但 JavaScript 的单线程模型让 Rollup 只能一个一个解析、一个一个处理。Rolldown 在模块分组、依赖解析、transform 和 minify 阶段都做了并行化处理在 8 核以上的机器上优势尤其明显。第三端到端链路更短。过去从源码到产物要经过esbuild 转译 AST - 生成 ESM - Rollup 再解析 - 重新建图 - 再生成产物这样多轮 parse-generate 循环。Rolldown 全程只解析一次源码AST 和语义信息在转译、图构建、tree-shaking 阶段反复复用最终才输出产物。省掉中间环节代表省掉了大量重复计算。理解了这个原理也就理解了一个常见误区Rolldown 快不是靠什么黑魔法而是把以前浪费在两套引擎之间倒腾和Node.js 单线程上的性能缺口全部补了回来。3. 实测环境与基准项目3.19 倍是怎么测出来的3.1 测试项目和硬件环境这次实测我没有选一个 demo 级的小项目而是直接拿我们团队在维护的中后台管理系统做的。项目规模分布如下项目维度具体数值技术栈React 18 TypeScript Ant Design页面路由数约 320 个全部采用懒加载业务模块imported 模块数约 2100 个node_modules 依赖包数量约 1300 个静态资源图片约 600 张含部分大图入口1 个 HTML 3 个 JS entry硬件环境是 MacBook Pro 14 英寸M3 Pro 芯片18GB 内存macOSNode.js 22 LTS。这个配置不算高配尤其 18GB 内存在跑大项目时已经比较紧张所以测出来的数据对大多数开发者的参考意义反而更大。测试对象是 Vite 7.1.6双引擎基线和 Vite 8.0Rolldown 默认引擎。Vite 7 时期的生产构建使用的是 Rollup 4 esbuild minify而 Vite 8 的项目构建链路几乎全部跑到 Rolldown 上。3.2 测量口径与指标定义性能测试最怕口径不统一。为了避免被感觉快了误导我严格规定了三种测量方式冷启动时间清空 node_modules/.vite 缓存后执行 dev 脚本从进程启动到日志里出现ready in xxx ms记为一次。测 5 轮取中位数。生产构建全量先rm -rf dist再执行vite build从进程启动到构建命令退出记为一次测 5 轮取中位数。构建阶段细分只统计 parse、bundle、minify 三个阶段通过DEBUGrolldown:*日志和 hook 埋点分别计时。实测数据如下指标Vite 7.1.6Vite 8.0提升幅度冷启动 dev server8.6s2.4s约 3.58 倍生产构建全量rm dist 后38.4s12.1s约 3.19 倍增量构建watch 模式第二次12.7s4.1s约 3.10 倍构建产物总体积18.2MB17.6MB产物还小了 3.2%需要说明的是3.19 倍这个数字是全量生产构建时间之比。如果你只看 minify 或者只看打包阶段提升幅度会有差异但整体结论非常一致量级上的提升是真实的不是某个阶段赢了、另一个阶段拖后腿的结果。4. 拆解构建链路快在哪几个环节4.1 依赖预构建与冷启动从 8.6 秒到 2.4 秒开发冷启动是体验最直观的一项。Vite 7 冷启动时esbuild 需要把几百个 node_modules 里的依赖重新预打包项目越大这个时间越明显。我这边 Ant Design 加上一堆工具库esbuild 预构建差不多要跑 3 到 4 秒然后再进入源码模块的链接和 transform整体到 ready 是 8.6 秒。Vite 8 的 Rolldown 处理同样工作只需要不到 1 秒。原因是它复用第一次构建的模块图缓存预构建阶段和源码构建阶段共享同一套分析结果不需要像之前那样一边用 esbuild 打包依赖、一边用 Vite 插件系统处理源码双重解析。最终冷启动降到 2.4 秒哪怕项目里路由再多也是按下启动键还没拿起水杯就 ready 了。4.2 生产打包tree-shaking 与 chunk 拆分提速生产构建的 38.4 秒到 12.1 秒大头在 Rollup 主打包环节。之前的 Rollup 4 在这个项目上做 tree-shaking 和 chunk 拆分需要 20 秒以上因为它是纯 JS 单线程硬啃 2000 多个模块的依赖图。Rolldown 在这部分的耗时大概是 6 到 7 秒把最重的活并行化之后直接在原来的三分之一到四分之一之间。有一个值得关注的细节Rolldown 做 tree-shaking 时不是简单照搬 Rollup 的策略而是对sideEffects、moduleSideEffects和usedExports做了更细的融合判断。实测产物体积比 Rollup 时代还小了 3.2%说明 treeshaking 的保真度没有因为速度提升而降级反而因为语义信息更完整删得更加干净。4.3 代码压缩内置 oxc minifier 的实际表现代码压缩是另一个容易感知差异的点。在 Vite 7 里build.minify默认是esbuild速度快但压缩率相对一般如果追求极致产物体积就得切到terser但 terser 在这个项目上花 6 秒多很多人接受不了。Vite 8 的 Rolldown 内置了 oxc minifier我在同样项目上做了三种压缩器的对比压缩器耗时主 chunk 压缩后体积esbuild2.3s2.87MBterser6.1s2.71MBoxcRolldown 内置2.8s2.72MBoxc 的压缩耗时只比 esbuild 多 0.5 秒但压缩率已经非常接近 terser。对于生产构建来说最终产物体积比 minify 耗时更关键所以内置 oxc 基本就是最优解不再需要为了压缩率去做慢工出细活的痛苦取舍。5. 升级 Vite 8 的兼容性排查踩坑与解法5.1 插件生态的兼容性现状听我吹了这么多也得说说升级过程中没那么顺的地方。Rolldown 的插件 API 尽量兼容 Rollup但尽量和完全一致之间有差距。官方给出的标准说法是绝大多数vite-plugin-*和rollup-plugin-*可以在不做任何改动的条件下工作但涉及虚拟模块前缀、CommonJS 互操作、自定义 resolve 行为这三类场景时行为差异更容易冒出来。我这次测试的两个项目里小项目几乎是无缝迁移中后台项目则踩了三个实质性问题。下面按排查链路完整复盘。5.2 问题一虚拟模块前缀引发的解析失败症状项目里用到的vite-plugin-svg-icons在 Vite 8 下报错页面白屏控制台提示某个以virtual:开头的模块无法解析。排查过程先看 dev 端日志发现 Rolldown 把报错定位在resolveId阶段插件返回了一个\0开头即 null 字节前缀的虚拟模块 ID。过去 Rollup 对这类 ID 的容错比较宽松其他插件拿到手后即便没有显式处理也能被动透传。Rolldown 的处理更加严格如果 load 钩子里没有主动处理该前缀就会判定为未解析模块而直接抛出错误。最终解法升级vite-plugin-svg-icons到适配 Rolldown 的版本该版本在内部增加了一个 alias 层。如果你的项目里也有类似的自定义插件排查思路可以复制先看虚拟模块 ID 是否以\0或virtual:开头再看load钩子是否针对该 ID 做了返回。最简单的临时方案是在resolve.alias里把这类前缀映射到真实文件路径。5.3 问题二CommonJS 互操作差异症状某几个老组件库Webpack 时代发布的包在 dev 模式下跑得好好的生产构建产物却报了TypeError: xxx_default is not a function。单元测试和本地 dev server 都正常只有线上产物出问题排查起来很费劲。排查过程先导出 production build 的 chunk 文件找到报错位置发现某个模块的default导入被解析成了一个对象而不是函数。深入后发现这是 Rolldown 对 CommonJS 模块的interop处理语义与 Rollup 不同。Rollup 默认的互操作偏向于兼容老模块很多情况下会把module.exports直接当作default导出Rolldown 则更贴近 Node.js 原生 ESM 的互操作语义在判断模块是纯 CJS 还是被打包工具转成 ESM时结论不一样。最终解法在build.commonjsOptions中显式设置defaultIsModuleExports: true让 Rolldown 对指定范围的老依赖采用宽松模式。这类问题不用追求全局复制旧行为只对出问题的依赖做定向处理即可避免影响其他模块的 tree-shaking 效果。5.4 问题三chunk 切割策略变化导致产物清单变化症状升级之后构建完全没有报错但产物目录里的 chunk 名称和数量跟 Vite 7 完全不同。部分 vendor 包被拆得更碎统计平台和 CDN 缓存策略受到一定影响。排查过程这个其实不是 bug而是 Rolldown 的默认 chunk 切分策略比 Rollup 更激进。Rollup 倾向于按node_modules里每个包独立成 chunk而 Rolldown 会结合实际依赖图热度做更动态的合并拆分所以 chunk 列表发生变化是预期行为。最终解法我没有试图把产物还原成 Rollup 的样子而是用manualChunks固定了几个核心 vendor 组react 全家桶、antd 全家桶、业务公共模块。其余交给 Rolldown 自动处理。这样既保住了长缓存策略又保留了速度优势。如果你对产物列表有强校验升级时记得在 CI 里加入 chunk 清单 diff 检查。5.5 关于 minify 切换别硬写 terser前面热词里有人问minify: terser和minify: esbuild的区别放到 Vite 8 语境下还得再加一个选项。Vite 8 里不再建议显式指定terser。虽然 Rolldown 为了兼容也保留了调用 terser 的能力但那会让构建走回 6 秒级别的压缩路径把换芯带来的收益吞掉一大截。实测表明 oxc 的压缩率已经接近 terser除非你有特殊的压缩规则需要比如某些自定义 mangling 配置否则默认就好。.env或者vite.config.ts里写minify: esbuild的旧配置在 Vite 8 中会被忽略或降级处理不会报错但不起作用。迁移时建议直接删掉这行让 Rolldown 用内在压缩器。6. 升级建议与配置清单6.1 哪些项目值得第一时间升级根据实测经验我给出的判断标准比较明确强烈建议升级生产构建耗时超过 30 秒、模块数超过 1500、开发冷启动长期在 8 秒以上的中大型项目。这类项目是换芯收益最大的群体实测可以在不碰业务代码的前提下拿到 3 倍速。可以升级但要谨慎重度依赖大型 Rollup 插件如大量自定义 transform、代码注入、专用 babel 插件链的项目。先做一次插件清单 compatibility check再决定是否切换。暂缓升级的情况项目中存在大量 3 年以上未更新的 CJS 老依赖、且已经用rollup-plugin-commonjs等桥接手段硬撑的项目。这类项目需要先处理 interop 问题否则升级成本可能比收益还高。6.2 我推荐的 Vite 8 生产配置模板这是我这次升级后稳定运行的生产配置可以直接抄走import { defineConfig } from vite import react from vitejs/plugin-react export default defineConfig({ plugins: [react()], build: { // 不写 minify 也行默认走 rolldown 内置 oxc 压缩 // 如果显式写建议只写 oxc target: es2020, sourcemap: false, cssMinify: lightningcss, rollupOptions: { output: { manualChunks(id) { if ( id.includes(node_modules/react) || id.includes(node_modules/react-dom) || id.includes(node_modules/react-router) ) { return react-vendor } if ( id.includes(node_modules/antd) || id.includes(node_modules/ant-design) ) { return antd-vendor } if (id.includes(node_modules/echarts) || id.includes(node_modules/zrender)) { return echarts-vendor } // 其余依赖让 Rolldown 自动处理不用像 Rollup 时代拆得那么细 } } } }, server: { warmup: { clientFiles: [./src/main.tsx, ./src/router.tsx], files: [./src/layouts/*.tsx, ./src/pages/**/*.tsx] } } })几个解释manualChunks我只固定了体积最大的三个 vendor 组其他零碎依赖交给 Rolldown 的自动策略实际产物反而更均衡。server.warmup是 Vite 启动时预热常用模块的优化项配合 Rolldown 的并行解析冷启动上还有额外收益。6.3 灰度策略与回滚预案升级构建链路这种改动最忌讳一把梭。我推荐按这个节奏来在独立分支上升级vite和相关插件跑完整测。在 CI 里同时保留旧的 Vite 7 构建任务和新的 Vite 8 构建任务对比产物大小、chunk 数量、静态资源指纹。用新的构建任务部署到预发布环境观察 24 小时内的前端错误率和首屏 LCP 指标。如果出现兼容性问题且 30 分钟内定位不到根因直接回滚package.json中 vite 版本到7.1.6构建命令完全不用改产物回到旧内容。上线稳定一周后再清理旧构建脚本。回滚预案之所以要提前定死是因为换芯带来的 charm 列表变化可能影响发布监控、CDN 缓存策略、甚至后端网关的静态资源过滤规则。这些不是构建失败那样显性的故障而是隐性风险最好通过灰度流程先观察一轮。我在这次升级里还踩了一个小坑也分享出来rollup-plugin-visualizer和分析构建产物的工具需要显式升级到支持 Rolldown 的版本否则它会尝试走旧的 Rollup 回调 API导致产物分析图拿不到数据。排查时一度以为是新引擎没生成 sourcemap结果只是插件版本太老。总的来说Vite 8 换芯到 Rolldown 不是一次常规的版本大升级而是把构建链路从架构层面统一了。3.19 倍这个数字背后是开发和生产两条链路从双引擎倒腾走向单引擎全链路的必然结果。我个人实际体验中最意外的收获反而不是构建快了而是 dev 和 build 的产物行为越来越一致很多以前开发正常、构建翻车的历史遗留问题换芯后从根上消失了。如果你也在维护一个构建耗时让人头疼的项目Vite 8 值得尽快排进升级计划升级前重点检查插件版本和 CommonJS interop 配置其他交给 Rolldown 就好。最后再分享一个排障小技巧Rolldown 的调试日志比 Rollup 直观得多遇到拿不准的构建差异先DEBUGrolldown:* vite build跑一遍定位到具体阶段再动手改配置比对着产物猜要快得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价