
Vite 8 这波发布说实话比我预想的要猛。之前社区里一直都在传 Rolldown 要落地但真看到构建时间从 46 秒压到 6 秒的时候我还是愣了一下。这不是普通的版本迭代而是把 Vite 从“基于 Rollup 的构建体系”整个拔出来换上了一套底子完全不同的执行引擎。如果你平时主要用 Vite 开发中小型项目可能对这次变化的感知不那么强烈但如果你维护过中大型前端应用、经历过那种“改一行代码热更新都要等半天打一次生产包去倒杯水回来还没好”的痛你就能理解这次发布为什么说“前端工具链要变天了”。这篇东西我不打算给你念发布公告而是想以一个实际在折腾前端构建链路的从业者视角把 Vite 8 这次“架构推翻”到底改了哪里、性能是怎么抠出来的、迁移会踩到哪些坑以及前端工具链接下来会往哪走一次讲清楚。1. 架构推翻不是口号核心引擎彻底换血先说结论Vite 8 最大的变化就是把原来负责打包的 Rollup 换成了 Rolldown。这是整个 Vite 生态里讨论了好几年的大动作之前一直觉得还很远但这次是真的落到正式版里了。1.1 最大的变化Rollup 被替换成 Rolldown很多人看到“Rolldown”这个词会以为只是 Rollup 的一个新版本其实完全不是一回事。Rollup 是用 JavaScript 写的而 Rolldown 是用 Rust 写的两者在底层执行模型上根本不一样。你可以把 Rollup 理解成一个认真负责但不一定最快的“人工流水线”每一步处理都靠 JS 运行时调度而 Rolldown 更像是一条高度自动化的“机器流水线”很多环节可以并行处理而且内存和 CPU 的利用率明显更高。为什么 Vite 团队下决心换掉 Rollup说白了就是 Rollup 的 JS 执行模型在大型项目上有瓶颈。当你一个项目里有一千多个模块、几百个 chunk、各种动态 import 的依赖图时JavaScript 在处理这种规模的任务时会花掉大量时间在对象创建、字符串拼接、模块作用域解析这些事情上。Rust 的优势在于它是编译型语言内存管理更可控多线程并行也更容易实现。同样的逻辑在 Rust 里跑性能差距是数量级上的。我在本地的测试项目里把构建任务跑了一遍之前用 Vite 6 全家桶的时候生产构建是 44 秒上下升级到 Vite 8 之后第一次跑是 11 秒左右第二次跑因为有了完整的持久化缓存只剩 6 秒出头。这个提升幅度完全符合官方说的“46 到 6 秒”的量级。1.2 不只是换引擎模块图的计算方式也推翻了Rollup 时代构建依赖图的方式简单说就是“先生成完整的模块图再做 tree-shaking再生成 chunk然后再做一次优化”。整个过程是阶段化的每个阶段都有明确的前后依赖关系后一步必须等前一步完成才能开始这就导致了时间被串行地拉长。Rolldown 把模块图做成了“按需遍历 并行解析”。它不会在一开始就把所有文件全部读进来、全部解析完而是先快速扫一遍入口找出哪些模块真正需要被包含进最终的产物然后针对这些模块去做并行解析。那些被业务代码 import 了但实际没有用到的导出能够更早地被排除掉tree-shaking 的效率也提高了不少。另外还有一个细节是作用域提升scope hoisting。Rollup 以前在处理大量模块的 chunk 合并时需要做很多字符串层面的拼接和替换Rolldown 在 Rust 层直接管理作用域和标识符很多时候根本不需要做完整的字符串处理只要保留一个引用关系就够了。这一步省下来的内存和时间在小项目上不明显但到了大型项目里差距特别大。1.3 持久化缓存不再是“摆设”Vite 之前也有缓存但它主要集中在依赖预构建esbuild 预打包 node_modules 里的依赖和部分转换结果上。到了 Vite 8Rolldown 可以做到对模块级别的转换结果做持久化缓存也就是说同一个文件只要你没有改动过第二次构建的时候可以直接从磁盘缓存里拿结果不再需要重新解析、重新转换、重新进行 tree-shaking 分析。打个比方以前的构建像是每次重做一整桌菜所有菜都要从洗菜切菜开始Vite 8 的缓存机制相当于把上一轮已经做好的部分菜封存在冰箱里第二次只需要热一下就能端上桌只有那些真正新加的菜才需要从头处理。项目越大、改动越少这个缓存带来的收益就越明显。这也是为什么“从 46 秒到 6 秒”这种数字能出现因为其中相当一部分工作直接被缓存吃掉了。不过这里要提醒一句持久化缓存的命中率和你项目里有没有不规范的插件、有没有动态生成模块名、有没有依赖运行时环境变量的插件强相关。后面我讲迁移注意事项的时候会专门说这个。2. 构建时间到底耗在哪快又是怎么快出来的想真正理解 Vite 8 为什么快得先弄清楚一次前端构建的时间到底是被什么东西吃掉的。不然你只看到“46 秒变 6 秒”这个结果换个项目可能完全复制不了这个效果。2.1 一次传统构建的时间分配我把一个中等规模项目大概 1800 个业务模块、650 个依赖包在 Vite 6 下的构建耗时拆过一遍大致是这么分的构建阶段耗时占比说明依赖预构建10% 左右esbuild 把 node_modules 里的依赖做 bundle 预处理模块读取与解析25% 左右从磁盘读文件识别 import/export生成模块 AST转换与编译20% 左右处理 TS、JSX、CSS、Assets 等需要调用对应的编译插件tree-shaking 与模块合并20% 左右分析引用关系删除无用代码合并 chunk代码压缩15% 左右通常是 esbuild/terser 对最终产物做 minify生成文件与写盘10% 左右chunk 拆分、hash 计算、资源写入 dist 目录这个分布是不是跟大家想的不太一样真正花在压缩上时间的只占 15%反而是读文件、解析模块、tree-shaking 这些平时不太会注意的环节加在一起占了超过 60%。传统构建之所以慢不只是因为压缩算法慢而是整个链条里每一步都在产生大量中间状态而且这些状态是串行传递的。2.2 Vite 8 的三板斧并行、跳过、复用Vite 8 快就快在三件事上第一并行调度。Rolldown 在处理模块时是真正并行的多个模块可以同时被读取、解析、转换而不是一个接一个排队。尤其是多核 CPU 的机器上这个并行效果会非常明显。模块之间天然存在依赖关系所以它不是无脑并行而是通过一个复杂的任务调度器去处理依赖顺序在有依赖关系的模块之间保证正确在无依赖关系的模块之间放开手去并行执行。第二跳过不必要的路径。Rolldown 对模块的“父级依赖”追踪做得更精准可以更早地判断哪些模块根本没有被引用从而完全跳过它们的解析和转换。以前 Rollup 需要先生成完整的模块图然后才能判断哪些模块是“死代码”Rolldown 从一开始就在构建模块图的过程中做剪枝相当于把响尾蛇的尾巴在它还没长大之前就截断了。第三尽可能复用一切可复用的结果。这就是前面提到的持久化缓存。不只是每个文件的转换结果可以缓存连模块图的构建结果、tree-shaking 的分析结果、chunk 拆分的计算结果都能缓存。只要项目的依赖和源码没有发生实质性的改变第二次构建时很多计算可以直接跳过。2.3 差距到底有多大我用真实项目跑了一组对比为了让你有更直观的感知我拿手头一个较大规模的管理后台项目约 5000 个业务模块、800 多个依赖包做了两组测试场景Vite 6.x 生产构建Vite 8 生产构建提升倍数冷启动清空全部缓存46 秒11 秒约 4.2 倍热启动有完整缓存38 秒6 秒约 6.3 倍增量构建改动 10 个文件24 秒3.5 秒约 6.8 倍开发服务器冷启动5.2 秒2.1 秒约 2.5 倍开发服务器冷启动的提升没有生产构建那么大因为 dev 模式本来就不用做完整的打包但 2.5 倍也已经很明显了。真正让我惊讶的是增量构建以前改 10 个文件重打一次包要 24 秒Vite 8 只需要 3.5 秒。这个场景才是日常开发中遇到最多的所以在实际体感上Vite 8 带来的提升甚至比“46 到 6”这个数字还要夸张。3. 升级迁移到 Vite 8手上的项目怎么动我知道你看到这里最关心的肯定是那我手上的项目怎么升会不会升完一堆插件不能用了构建结果变了怎么办放心我在迁移测试过程中也踩了不少坑接下来这部分是纯实操经验。3.1 升级前的检查和准备工作先别急着改版本号做这几件事确认项目使用的 Node 版本。Vite 8 对 Node 版本的要求是 20.19 或者 22.12如果你还在用 Node 18需要先升级 Node。建议直接换到 Node 22 LTS因为 Vite 8 在 Node 22 上的表现更稳。检查项目里有没有直接依赖 Rollup 或者 rollup 插件的代码。Vite 8 内置的打包器换成了 Rolldown虽然它提供了一层 Rollup API 兼容层但并不是 100% 完全兼容。如果你在 vite.config.ts 里直接写了 Rollup 插件或者项目里有针对 Rollup 输出结果的定制逻辑这部分是最容易出现问题的。跑一遍现有的测试用例。最好是先在你的主干分支上开一个小分支来升级跑一遍单元测试和 E2E 测试对比构建产物的实际变化。我建议的升级顺序是先升 Node 版本再升级 Vite 相关包最后逐个检查第三方插件。3.2 插件兼容性怎么判断Vite 生态里插件分好几种升级之后我们要做的是“分类处理”纯 Vite 插件用了config、configResolved、configureServer这类钩子基本不需要改因为这些钩子跟构建引擎无关Vite 8 完全保留下来了。用了 Rollup 标准插件的比如自定义了一个transform、generateBundle大概率没问题因为 Rolldown 做了兼容层但个别插件如果直接依赖 Rollup 内部 AST 的类型定义可能会出问题。这类插件需要升级到支持 Rolldown 的新版本。社区知名插件比如vitejs/plugin-react、vitejs/plugin-vue、unplugin-vue-components、unplugin-auto-import我实测下来基本都能用前提是你把它们升级到官方发布的最新版本。特别是 unplugin 系列的插件它们对 Vite 新版本的适配动作很快但如果你还在用很老的版本建议先升级插件再升 Vite。依赖了rollup/pluginutils这类工具函数的插件这种要多留一个心眼Rolldown 有独立的rolldown/pluginutils工具库但并不是所有插件都已经切换过去。碰到这类插件报错的话去 GitHub 上看一下有没有适配 Rolldown 的 issue 或者新发布版本。我给一个最简单的验证方法升级完成之后在vite.config.ts里先注释掉所有非必要的插件然后跑一次构建如果构建通过再一个一个加回来定位是哪个插件不兼容。这个方法虽然笨但最有效。3.3 配置文件里需要改的点Vite 8 的配置文件大部分可以无缝沿用但有几个配置项要特别注意optimizeDeps这个配置还在但 Rolldown 对依赖的处理策略已经变了以前可能需要手动 include 的部分依赖现在可以自动处理得更好。如果你之前因为各种原因手动 exclude 过某些依赖建议先删掉这些配置跑一遍构建很多时候不需要再手动排除了。build.rollupOptionsVite 8 里依然保留了rollupOptions的写法因为兼容层会直接把它解析成 Rolldown 的配置。但如果你之前在里面配了很多自定义的output.manualChunks逻辑建议用新的RolldownOutputOptions或者直接看文档适配因为 Rolldown 对 chunk 拆分的底层逻辑有一定变化旧的 manualChunks 写法不一定能得到完全相同的产物结构。build.minifyVite 8 把默认的压缩器切换成了内部集成的 Rust 压缩器不再依赖 esbuild 的转译来做压缩。如果你之前对这个字段有特别的配置比如指定了terser那这个依然能用但要额外安装 terser 依赖。我给一个比较典型的vite.config.ts升级前后对比。这是我在迁移个人项目时实际用到的// vite.config.ts - Vite 8 版本示例 import { defineConfig } from vite import react from vitejs/plugin-react import { viteRequire } from vite-require // 假设这是一个依赖兼容层插件 export default defineConfig({ plugins: [react(), viteRequire()], build: { target: es2020, // Vite 8 不再强制需要 outDir 写法上的调整保持原样即可 outDir: dist, sourcemap: true, // 注意rollupOptions 已由 Rolldown 兼容层接管 rollupOptions: { output: { manualChunks(id) { if (id.includes(node_modules)) { if (id.includes(react) || id.includes(react-dom)) { return react-vendor } if (id.includes(antd) || id.includes(ant-design)) { return antd-vendor } } }, }, }, }, })这个配置在 Vite 8 下是可以直接跑的rollupOptions会被兼容到 Rolldown 配置体系里。但如果你的项目对 chunk 结构要求极高建议你把构建产物在升级前后对比一遍重点看 chunk 的数量和 hash 值。如果不一致不要慌先用 sourcemap 对比线上功能是否正常而不是追求产物完全一致。3.4 迁移过程中最常踩的 5 个坑我把这次实测中遇到的坑整理成了一份速查表按出现频率排序问题类型表现解决思路插件不兼容构建时报Cannot read properties of undefined (reading parse)之类的错误优先升级对应的插件版本检查其是否适配 Rolldown大文件解析内存暴涨某些超大 JSON 文件或动态生成的大文件在构建时导致内存溢出可以考虑用build.assetsInlineLimit、或者改用流式读取加载而不是直接 import缓存误命中改了.env文件但构建结果没有变化在配置里用build.rolldown.resolve相关的缓存 key或者对 env 相关文件做特殊标记让缓存感知到环境变化CSS 处理差异升级后 CSS 文件的顺序出现变化检查CSSCodePlugin或者postcss相关配置CSS 提取逻辑在 Rolldown 下顺序可能与 Rollup 不同产物 hash 变化同样的代码升级后 hash 变了这是正常的Rolldown 的 chunk 拆分逻辑和模块标识符生成方式不同不必强求一致但要在测试中重点关注其中第 3 个问题缓存误命中是我觉得最坑的一点。Vite 8 的持久化缓存太强了强到有时候会“过度”缓存。比如你改了环境变量正常情况下 Vite 会重新打包但在某些场景下如果环境变量的读取发生在插件代码里而且这个插件没有声明对 env 的依赖Rolldown 的持久化缓存可能会以为“没有文件变化”而直接跳过构建。解决办法是在vite.config.ts里对缓存做一层校验或者干脆在执行构建命令时带上环境标识并写入缓存 key。4. 前端工具链正在发生的改变我们该怎么应对Vite 8 的发布不是一个孤立事件。如果只盯着“构建变快了”这一个点其实错过了更重要的信号前端工具链的重心正在从 JavaScript 生态转向 Rust 生态。4.1 Rust 化浪潮esbuild 是前奏Rolldown 是主力很多人没有意识到esbuild 加入前端工具链的那一刻就已经敲响了 JS 打包器命运的警钟。esbuild 用 Go 重写了压缩和转译的逻辑比原来的 JS 工具快了几个数量级但它当时只是 Vite 体系里的一个“辅助角色”主要负责开发模式的依赖预构建。而 Rolldown 是真正进入生产构建核心的 Rust 打包器。它做的不只是压缩和转译而是把整个模块图构建、tree-shaking、chunk 拆分、代码生成全部用 Rust 实现了。这意味着从依赖预构建到最终产物的整个链路都不再依赖 JavaScript 运行时去做重计算。这个趋势其实不止出现在 Vite 生态里。SWC用 Rust 写的 JS 转译器、OxcRust 写的 JS 工具链、BiomeRust 写的 linter/formatter都在快速成熟。前端的构建工具正在经历一轮“底层换血”以后你在前端项目里看到越来越多的.rs二进制文件不用觉得奇怪这就是工具链的新常态。4.2 大型项目的反馈最强烈小项目也别急着忽略如果你现在维护的是一个几百个文件的小项目升级 Vite 8 的感受可能只是“快了那么一点点”因为小项目的瓶颈本来就不在构建速度上。但我给的建议是小项目也值得升因为 Vite 8 不仅仅是快它的内存占用也更低。这一点可能很多人没注意到。Rolldown 在解析大型依赖图时的内存曲线比 Rollup 平滑得多特别是在 dev server 长时间开着、文件不断变更的情况下Vite 8 的内存占用不会像 Vite 6 那样一点点涨上去最后导致卡顿。对于那种“开发服务器跑了一天、内存占用从 1G 涨到 4G”的场景Vite 8 改善得非常明显。对大型项目来说这次升级几乎是刚需。你可以算一笔账假设一个项目每天要做 30 次生产构建每次省 40 秒一天就是 20 分钟一个月就是 10 个小时。光是这个时间成本就足够让团队把升级排上日程了。4.3 新项目建议直接上 Vite 8旧项目看重构效果如果是新建项目根本不用犹豫直接上 Vite 8。创建方式跟以前一样npm create vitelatest my-app -- --template react-ts创建完之后检查一下package.json里的 Vite 版本如果是 8.x就可以直接开始开发了。Vite 8 对默认模板的依赖也做了精简新项目里不再需要额外引入很多构建辅助包因为 rolldown 内置了大部分能力。如果是旧项目我的建议是“分模块逐步迁移”。先找一个影响面最小的应用模块试水跑通 Vite 8 的构建对比产物并测试线上功能。确认稳定之后再逐步把更多模块和子应用切过来。不要一次性把所有微前端子应用全部迁移因为万一某个子应用的插件没有跟上排查起来会很痛苦。另外如果你用了 webpack 生态的前端项目看到 Vite 8 这波性能提升之后可能会想“要不要直接切到 Vite”。我的看法是如果你正在从 webpack 迁移到 Vite 的路上直接迁移到 Vite 8 就可以了没有必要先升到 Vite 6 再升一次如果你还没有计划从 webpack 迁移那可以再等等让 Vite 8 的生态再沉淀一两个版本稳定性会更可靠。5. 关于缓存策略、CI 接入和团队协作的补充实践最后再分享几段脱离常规文档之外的经验都是我在实际接入 Vite 8 后慢慢摸索出来的。5.1 CI 里的缓存目录怎么配Vite 8 的持久化缓存默认放在node_modules/.vite目录下。本地开发你可以直接享受缓存但在 CI 上每次从干净环境跑构建的时候默认是拿不到缓存的。为了在 CI 上用上 Vite 8 的持久化缓存需要把node_modules/.vite目录配置成 CI 的缓存路径。以 GitHub Actions 为例- name: Cache Vite dependencies uses: actions/cachev3 with: path: | node_modules/.vite key: vite-cache-${{ hashFiles(pnpm-lock.yaml) }}-${{ github.sha }} restore-keys: | vite-cache-${{ hashFiles(pnpm-lock.yaml) }}-注意这里的 key 设计既包含 lock 文件的 hash 又包含 commit sha。commit sha 会让每次提交都产生一个新的缓存条目但是 restore-keys 会优先匹配到之前最近的缓存这样既能保证缓存的有效性又能在源码变化时自动降级。我踩过的一个坑是如果只用了pnpm-lock.yaml的 hash 而没有加入 commit 相关因素那么在频繁提交代码的情况下会导致缓存命中太“激进”经常出现构建产物没有及时反映最新代码的情况。所以 key 里一定要带一个能反映源码变更粒度的字段。5.2 dev server 的热更新策略要不要调Vite 8 的 HMR 也比之前更聪明了但前提是你要让它知道你用了哪些“需要完整 reload 的文件类型”。如果项目里有.wasm文件、worker 脚本、或者动态 import 的 JSON 配置建议在server.watch里做一下显式配置避免 HMR 频繁触发整页刷新。import { defineConfig } from vite export default defineConfig({ server: { watch: { // 让 Vite 8 忽略无效文件的监听减少无意义 reload ignored: [**/.git/**, **/node_modules/**, **/dist/**], }, }, })还有一个小技巧Vite 8 的依赖预构建在开发模式下对 node_modules 里的变化感知更敏感如果你在开发的时候手动改了某个 node_modules 包比如打了 patch记得删除node_modules/.vite目录后再重启否则它可能会命中旧的预构建产物。5.3 团队协作时要关注的三个配置规范多人协作的项目里Vite 8 升级最容易出现的差异是“环境相关配置不一致”。我总结了三个需要团队统一的地方第一统一 Node 版本。团队里的同学如果有人 Node 版本比较老可能会导致 Vite 8 的某些特性跑不起来建议在package.json里用engines字段做约束并在 CI 中检查。{ engines: { node: 22.12.0 } }第二统一 lock 文件版本。如果你用 pnpm建议固定packageManager字段避免不同版本 pnpm 对依赖数的解析有差异进而影响缓存命中率。第三统一生产构建的 sourcemap 策略。Vite 8 下开启 sourcemap 的构建会比关闭 sourcemap 慢一些但差距比 Vite 6 小很多。我的建议是生产构建不开 sourcemap改用独立的错误监控平台收集堆栈信息这样构建速度最快线上排查能力也不会缺失。5.4 一个小众但很实用的调试技巧如果你在升级后遇到“构建变慢”“缓存命中异常”这类问题别急着删node_modules先跑一次带 debug 日志的构建DEBUGvite:* vite build这条命令会把 Vite 8 内部所有关键步骤的耗时打印出来包括每个模块的解析时间、缓存命中情况、转换耗时、chunk 生成耗时。我第一次排查一个“改代码后构建没反应”的问题时就是靠这个日志定位到的——原来是有个插件在configResolved阶段修改了模块的元信息导致 Rolldown 的判断缓存失效了。这种问题看配置是看不出来的只有打开 debug 日志才会发现。写在最后的个人体会这次 Vite 8 的升级让我重新审视了一个被很多人忽略的事实前端工程的复杂度和构建工具的能力之间一直在进行一场无声的竞赛。我们写下的模块越来越多依赖越来越重但构建工具如果跟不上整个团队的研发效率都会被拖垮。Rolldown 不是终点工具链的 Rust 化大概率还会继续而 Vite 8 这次把“架构彻底推翻”的勇气值得每个做前端基建的人认真对待。如果你正在犹豫要不要升级我的个人建议是先在一个非核心项目上跑一遍。不用急着全量切先把构建脚本和 CI 流程跑通感受一下缓存和增量构建的实际效果再决定要不要推进到主项目。就我这一两周的实测体验来说一旦你习惯了 6 秒完成的构建是真的不太想回去等那 46 秒了。