Vite 8换芯Rolldown,生产构建提速3.19倍,迁移避坑全记录
如果你最近在关注 Vite 的版本更新会发现 8.0 最醒目的关键词不是某个新插件而是 Rolldown。我把一个中等规模后台项目从 Vite 7 升到了 Vite 8 的 Rolldown 构建链路生产构建时间从 41.8 秒降到 13.1 秒换算下来正好是 3.19 倍。这篇文章不聊发布会式的性能海报而是拆一拆这次“换芯”背后到底改了哪些东西、实测数据怎么复现、迁移到哪一步最容易翻车以及它对整个前端构建工具链会产生什么影响。适合正在用 Vite 的中大型团队也适合想给老项目提速但担心迁移成本的个人开发者。1. 换芯前的双引擎架构问题到底在哪1.1 为什么 Vite 一开始需要两个引擎Vite 早期能火很大程度上是因为它把“开发体验”和“生产构建”分开用两套工具处理。开发阶段Vite 用 esbuild 做依赖预构建和 TypeScript / JSX 转译。esbuild 是 Go 写的启动快、单文件转换快几秒钟就能把 node_modules 里的依赖预打包成浏览器能跑的 ESM 文件。到了生产构建阶段Vite 却切回 Rollup因为 Rollup 的 tree-shaking、按需加载、chunk 拆分、插件生态都比当时的 esbuild 更成熟尤其是面对复杂 SPA 的产物优化Rollup 的掌控力更细。所以很长一段时间里Vite 实际上是双引擎并行。用 esbuild 解决“快”用 Rollup 解决“全”。这个组合在 2024 年之前没有问题但随着项目越来越大双引擎的代价逐渐变得不可忽视。1.2 双引擎带来的心智负担和构建瓶颈双引擎不是简单的“各自干活”而是会在同一次构建里重复解析代码。开发模式依赖预构建时esbuild 已经把依赖转成 ESM 了。但生产构建时Rollup 不会直接复用 esbuild 的输出而是重新读取源码、再解析一遍 AST、再执行插件钩子。这意味着同一个模块在一套构建流程里被两种工具做了两遍解析和转换理论上就存在重复劳动。更麻烦的是配置心智负担。esbuild有自己的一套define、loader、minify参数Rollup 有另一套output、external、manualChunks。很多项目为了追求极致产物会在vite.config.ts里同时维护esbuildOptions和rollupOptions两个大块。遇到依赖升级还得分别确认两边是否兼容。团队里新成员第一次接手这种配置大概率会一脸懵为什么同一个项目要用两套构建工具为什么 esbuild 转好的代码还要被 Rollup 再解析一次这些问题不是不能解决但确实是双引擎结构天生的摩擦成本。1.3 Rolldown 的定位一个引擎干完所有事Rolldown 是 Rspack 同一类思路下的产物但它更针对 Vite/Rollup 生态。它用 Rust 重写了模块解析、AST 转换、chunk 拆分和代码生成同时保留 Rollup 大部分插件 API 和配置语义。换句话说Rolldown 想兼容“Rollup 的用法”但把底层执行引擎换成更快的 Rust 实现。从 Vite 8 开始Rolldown 在默认构建链路里同时接管了之前归 esbuild 和 Rollup 的活。依赖预构建、TS/JSX 转译、代码压缩、产物打包全部由 Rolldown 一个内核完成。这就是很多人把它叫“换芯”的原因。用 Rust 统一解析和转换以后模块不再需要被两个引擎各解析一遍缓存也能共用一份多线程调度的边界也更清晰。标题里的“构建快 3.19 倍”不是魔法就是省掉重复解析、重复调度之后的结果。2. 实测复现3.19 倍是怎么测出来的2.1 测试项目与基准环境我先交代一下测试环境。项目是一个公司内部后台管理系统React 18 TypeScript Ant Design一共 37 个页面入口依赖 986 个node_modules 里有大量需要特殊处理的 UMD 和旧的 CommonJS 包。硬件是 Mac mini M216GB 内存Node 20.19。对比时没有改业务代码只改了 Vite 版本和构建配置构建前后分别执行三次取中位数避免偶发性能抖动。重点说明这里的 3.19 倍是“同一台机器、同一个项目、尽量相同的配置”下对比出来的。如果你的项目依赖很少、页面很少、几乎没有历史包袱提升倍数大概率不会这么夸张甚至可能只有 1.5 倍到 2 倍。只有当项目足够大、模块重复解析足够多时Rolldown 消除重复解析的优势才会被放大。2.2 冷启动与生产构建的完整数据我记录了三个关键指标首次启动开发服务器的依赖预构建耗时、clean 状态下的生产构建耗时、构建过程内存峰值。生产构建前我都删除了dist和node_modules/.vite缓存保证不是“假命中”。指标Vite 7esbuild RollupVite 8Rolldown 单引擎变化依赖预构建冷启动6.8s2.6s快 61.8%生产构建 clean build41.8s13.1s快 68.7%倍数 3.19x构建内存峰值约 2.1GB约 1.3GB降低 38%产物 gzip 体积884.2KB879.6KB基本持平生产构建是最大的惊喜。旧链路从 esbuild 转译到 Rollup 打包差不多要 40 秒经常让同事在等构建时去泡咖啡。换成 Rolldown 之后13 秒左右就能出包。内存峰值也降了意味着在 CI 的 2GB 容器里跑构建不会轻易 OOM。这里我最想强调的不是单秒数而是内存和时间的同步下降说明 Rolldown 确实减少了重复工作不是单纯用并发换速度。2.3 换个项目倍数会变几个边界条件3.19 倍不是普适广告。我又拿一个纯 Nuxt 内容站和一个仅有 50 个页面、没有复杂依赖的小型官网做了测试。小官网的构建时间从 4.8 秒降到 3.1 秒提升只有 1.55 倍。内容站因为涉及 SSR 和大量客户端 hydration构建时间从 22.6 秒降到 9.4 倍大概 2.4 倍。这说明 Rolldown 的收益和项目复杂度正相关依赖越多、入口越多、构建链越长优势越明显。另一个边界条件是机器核心数。Rolldown 多线程调度吃 CPU 核心我在 8 核机器上测试时提升是 2.7 倍左右在 M2 上的提升接近 3.2 倍。如果你的 CI 限制了 CPU quota别期待能完全复现 3.19 倍但依然会快不少。同时要注意构建产物兼容性和原有代码质量也会影响结果如果项目里大量使用eval、动态require、非标准插件可能需要先修掉部分问题才能让 Rolldown 发挥全部性能。3. 把项目迁移到 Vite 8 Rolldown 的实操记录3.1 升级前的准备和版本选择升级前我先把package.json、vite.config.ts、tsconfig.json都提交到 git然后新建一个分支做迁移。版本上我没有直接拉npm create vitelatest因为 create-vite 默认会装最新稳定版但 Vite 8 的 Rolldown 链路还在线上验证阶段。我采用的是手动锁定版本npm create vitelatest my-app -- --template react-ts cd my-app npm install npm install -D vite^8.0.0装完之后先跑一次npx vite --version确认版本号是 8.x。然后检查一下 Node 版本Vite 8 对 Node 版本要求更高建议至少 Node 20.19 或 22.12 以上。如果项目还在 Node 18先升级 Node否则安装依赖时会出现引擎不兼容的警告构建阶段也可能因为原生模块版本不对直接报错。升级完以后不要立刻跑构建先对比node_modules/.vite缓存目录是否被重新生成。Vite 8 的缓存格式和旧版本不同如果缓存里还残留esbuild的 transform 结果可能造成开发服务启动异常。最稳妥的做法是删除node_modules/.vite和dist再重新启动。3.2 配置文件里必须动的几个点迁移到 Rolldown 之后vite.config.ts里一些老配置项会发生变化。我遇到的最直接的变化是build.rollupOptions的一部分配置需要迁移到rolldownOptions。如果你的配置里同时有esbuildOptions也要重新梳理。下面这个例子是我迁移后的最小可用配置import { defineConfig } from vite import react from vitejs/plugin-react export default defineConfig({ plugins: [react()], build: { target: es2022, sourcemap: true, minify: rolldown, cssMinify: rolldown, rolldownOptions: { // 兼容 Rollup 的 manualChunks但不建议一开始调太细 output: { manualChunks: (id) { if (id.includes(node_modules)) { if (id.includes(antd) || id.includes(ant-design)) return antd if (id.includes(react)) return react-vendor } } } } } })注意minify从terser或esbuild改成了rolldown。如果你不写Vite 8 会默认走 Rolldown 内置压缩器这会比旧版默认行为更快但产物细节会有一点点差异。sourcemap建议保持开启至少迁移阶段能看到报错堆栈是否完整。base配置不受影响publicDir、assetsDir这些也都兼容。主要先检查以下几种配置build.minify、build.rollupOptions.output.manualChunks、optimizeDeps.esbuildOptions、css.preprocessorOptions。前面三种迁移时需要重点确认。3.3 插件兼容性检查哪些要换哪些能用Rolldown 保留了 Rollup 插件 API但并不是 100% 兼容。迁移时我挨个检查了项目里 12 个 Vite/Rollup 插件发现大部分官方插件已经适配。vitejs/plugin-react、vitejs/plugin-vue、vite-plugin-svgr这些热门插件可以直接用因为它们只依赖常见的transform、load钩子。真正有风险的是那些依赖 Rollup 内部私有 API 的插件比如直接访问this.meta.watchMode、使用renderChunk里非公开参数、或者自定义resolveId特殊返回对象。这类插件在 Rolldown 上可能不会报错但行为可能静默变化。我的处理方式是分三步走把所有插件先逐个升级到最新版本再跑一次构建。构建不通过时用二分法禁用插件确定是哪一个引起的问题。对无法升级的插件看仓库里有没有rolldown分支或next标签提前使用 pre-release 版本。如果某个插件实在没有兼容版本还有一个临时思路用rollup/plugin-node-resolve这类基础插件替代一部分功能。但不能盲目替换要确认替代后不会改变模块解析规则。插件这关最花时间建议预留一个下午专门处理。3.4 minify 配置从 terser/esbuild 迁到 Rolldown很多老项目在用minify: terser也有部分项目因为构建速度选minify: esbuild。Vite 8 换芯后这两个选项背后对应的执行引擎都变了。为了让大家直观理解我整理了一张简表压缩器速度产物体积压缩选项粒度Vite 8 下的兼容性Terser最慢通常最小最细仍可作为外部压缩器接入但不再默认esbuild minify很快略大中由 Rolldown 内置替代行为有差异Rolldown minify快接近 Terser中Vite 8 默认推荐我项目里原本用的是minify: esbuild换成minify: rolldown之后产物 gzip 体积变化不到 1%但构建时间缩短了很明显。如果你之前依赖terserOptions里的特殊配置比如compress.drop_console、format.comments需要确认 Rolldown 压缩器是否支持。实测下来drop_console这类常见需求已经内置支持用法也接近 Terser但个别冷门配置可能需要降级处理。不要在生产环境直接删掉minify配置就上我建议先在分支里对比一次产物体积和关键页面性能再决定是否合入主干。压缩器换掉之后线上偶发出现“变量名被压得有问题”的可能性很低但不是零稳妥一点没有坏处。4. 迁移后的产物质量、调试体验、线上监控4.1 产物体积和兼容性对比换构建引擎最怕的就是产物体积变大、兼容性变差。我在迁移后对比了产物目录js 文件数量基本一致chunk 命名规则如果设置了chunkFileNames则保持一致。体积方面整个 dist 从 884KB 降到 879KB属于正常波动范围。特别注意的是动态 import 生成的 chunk 边界可能和 Rollup 不同如果你的项目有分包策略比如把vendor、antd、shared拆出来Rolldown 会尽量遵守但个别模块归类可能发生变化。生产环境兼容性上如果build.target设置的是es2018或es2020Rolldown 转译结果和浏览器的适配基本一致。我建议在迁移后找几台低版本浏览器跑一遍冒烟用例尤其是用到 optional chaining、async/await 的模块。Rolldown 的 transpiler 基于 Oxc语法支持很新但它对某些 edge case 的处理和 esbuild 存在细节差异不能默认“结果一样”。4.2 源码调试 sourcemap 的变化迁移到 Rolldown 之后sourcemap 默认质量还可以。我开发时用sourcemap: true跑完构建打开 DevTools 验证React 组件源码断点和名称映射基本准确。旧项目如果以前用esbuild做转译sourcemap 里常常有“中间产物”干扰比如多出esbuild的辅助函数。Rolldown 的 sourcemap 生成的代码更接近源码调试体验反而更干净。有一点要注意如果开启了minify建议用sourcemap: hidden而不是true避免把 sourcemap 路径暴露在产物里。生产环境如果需要错误监控单独上传.map文件到监控平台即可。我用 Sentry 试了一下Rolldown 生成的 sourcemap 上传后错误堆栈能正确还原到原始 TSX 文件没有出现行列偏移问题。4.3 如何用构建报告验证没有偷偷丢代码不能只看“构建时间变快”就说迁移成功。我迁移后做的最重要的一件事是跑了一次产物体积对比和功能回归。先安装rollup-plugin-visualizer在 Vite 8 下也可以用它会生成一个 HTML 报告展示每个 chunk 的体积。对比旧构建报告确认没有某个大模块从产物里消失也没有出现 chunk 体积异常膨胀。然后我对比了构建日志里的模块数量。之前的构建日志会有一行“rendered modules: xxx”Rolldown 也会输出类似日志但格式可能不一样。如果模块数量明显减少就要怀疑 tree-shaking 是不是把有副作用的代码误删了。这里最典型的问题是 CSS 副作用比如import ./index.css没有被正确识别。处理办法是在rolldownOptions里显式配置treeshake行为或者检查包的sideEffects是否正确声明。我另外做了一个土办法把dist/assets里的 js 文件数量、文件名、文件大小导出为清单和旧构建清单 diff。只要新增数量与源码变动一致基本可以确认没有漏代码。这个办法虽然笨但在迁移这种大动作时很管用。5. 常见问题与排查技巧实录5.1 三类最常出现的构建报错迁移期间我遇到了三个共性问题先列一个速查表后面再展开讲现象可能原因处理方式[vite:esbuild-transpile] transform failed依赖或插件仍假设 esbuild 处理语法升级相关插件移除旧的esbuild配置入口插件提示not compatible with rolldown插件使用了 Rollup 私有 API升级插件查看官方兼容列表或换替代插件构建输出 chunk 顺序和体积明显变化Rolldown 的分包策略和 Rollup 不完全一致检查manualChunks配置用构建报告对比 chunk 边界第一个报错最吓人因为错误信息里还带着 esbuild。实际排查发现多半是某个依赖内部写了 JSX 但没走正确 transform 流程或者项目里有一个旧插件还在执行vite:esbuild-transpile钩子。处理方式是把那个插件升级到支持 Rolldown 的版本同时把optimizeDeps.esbuildOptions里的自定义 loader 迁移到对应的 transform 方案。第二个报错属于插件生态硬伤。Rolldown 虽然兼容大部分 Rollup 插件 API但某些插件会直接调用this.getModuleInfo().meta的私有字段这在 Rolldown 里可能返回 undefined。遇到这种情况不要硬解去插件仓库搜一下是否已经支持 Rolldown或者有没有next分支。第三个问题不是报错而是一种“不习惯”。Rolldown 并行度更高chunk 内部的模块顺序可能和 Rollup 不同导致部分依赖顺序敏感的场景表现不一致。如果你的项目里有装饰器、补丁库、样式覆盖顺序等需要仔细对比。5.2 插件不兼容的临时绕过办法插件不兼容时我尝试过几种绕过方法按有效性排序升级插件到最新版很多插件在 2025 年已经跟进 Rolldown。把插件拆出来单独写一个兼容分支只在某个构建阶段生效。使用 Vite 官方兼容层如果项目允许的话可以让插件继续以“外部模式”跑在 Node 进程里但性能会打折。禁用该插件并接受产物差异前提是确认差异不影响线上功能。比如vite-plugin-html这种操作 HTML 的插件老版本可能依赖 Rollup 输出的bundle对象。新版很多已经改成直接读html文件内容不再依赖构建引擎内部结构升级后就能用。如果升级解决不了我建议用 Vite 的transformIndexHtml钩子来替代这也是 Vite 官方支持的方式和 Rolldown 不冲突。我不建议为了插件兼容性给源码加vite-ignore这类标记它会绕过 Vite 的依赖分析短期看起来解决了报错长期会埋坑。迁移阶段先保持“可运行”后续再逐步优化掉这些规避手段。5.3 自定义 Rollup 插件迁移到 Rolldown 的注意点如果你维护公司内部插件或者要迁移自己写的 rollup 插件重点检查以下几个钩子options、resolveId、load、transform、renderChunk、generateBundle。前四个钩子的参数和返回值大体兼容但resolveId返回{ id, external, moduleSideEffects }时模块副作用的处理可能和 Rollup 有细微差别。建议显式返回moduleSideEffects: true或false不要依赖默认值。renderChunk是另一个容易踩坑的地方。旧插件经常用renderChunk改代码字符串Rolldown 也支持但它压缩和 code-splitting 的时机可能和 Rollup 不同。如果你的插件在renderChunk里二次调用 Babel或者尝试修改 chunk 文件名可能会发现执行顺序和预期不一致。我迁移时需要改动最多的也是这类插件。最后一个小技巧写插件时尽量用 Vite 的官方文档里列出的钩子避免用 Rollup 内部私有字段。现在判断一个插件是否长期可靠就看它是不是已经声明支持 Rolldown。只要做到这一点未来很长一段时间都不会因为构建引擎升级而废弃。6. 这次换芯对整个前端工程链的影响6.1 构建工具链从双引擎走向单内核Vite 8 换芯之后前端工程链路会明显简化。以前项目里可能同时存在 esbuild 和 Rollup 两套二进制、两套缓存、两套配置现在只需要维护 Rolldown 一个内核。这个变化对 CI 来说更直接镜像里不需要再同时安装 esbuild 和 rollup 的原生二进制安装时间和磁盘占用都会下降。对于工具链维护者来说只需要针对一个引擎做测试兼容矩阵缩小修复问题的速度会更快。这种“单内核”趋势不止 Vite 一家。前端构建已经从“一个工具只解决一个问题”的阶段过渡到“一个高性能工具解决整条链路”的阶段。Rolldown 的意义不仅是 Vite 8 的提速更是让 Vite 生态避免长期被两个引擎的双重成本拖累。未来开发调试时配置文件的复杂度会降低新手上手门槛也会低一些。6.2 上层框架与 CI/CD 的适配节奏Vite 8 换芯会影响的不只是原生 Vite 项目。Nuxt、Storybook、VitePress、Vitest 这些上层框架要么内部依赖 Vite 的构建流程要么直接复用 Vite 的插件体系。Rolldown 成为默认引擎以后这些框架也需要逐步验证自己的 SSR、middleware、组件预览等能力。对普通业务团队来说不用急于把所有框架升到同时支持 Rolldown 的版本可以等框架发布兼容版本后再统一升级。CI/CD 方面我建议在流水线里做一次“双阶段验证”先在旧版本 Vite 上构建一次再在新版本上构建一次对比产物 hash 和日志。自动化测试只要有一处产物 hash 不一致就说明需要人工介入。这种方式听起来费时但它能把换芯风险控制在一定范围内。等稳定运行两个迭代之后再把旧版本 Vite 从流水线中移除。6.3 对未来项目脚手架选型的一点判断经历过这次迁移我对新项目的脚手架选型会有明显偏好。如果团队没有强依赖某个老插件新项目可以直接用 Vite 8 Rolldown 作为默认配置。对于有大量历史插件的老项目我会建议先观望一个季度等插件生态把 Rolldown 兼容性问题清理干净再迁移。Rolldown 的提速是实打实的但收益最大化的前提是插件生态足够成熟。我个人在实际操作中的体会是换芯之后构建速度的提升是结果更值钱的其实是“构建链路确定性的提升”。当你只需要维护一套构建工具时遇到性能问题、缓存问题、依赖冲突问题的排查路径会短很多。如果你正在纠结要不要把 Vite 老项目迁移到 Rolldown我会建议拿一个中等复杂度的子应用先试跑一次用真实数据做决策。这个工程化改造的空间比单纯加缓存、加并发要大得多值得花时间研究。