Vite 8.0 发布:Rolldown 引擎重构构建链路,迁移指南与实测
Vite 8.0 的正式版本发布那天我朋友圈里好几个做前端工程化的朋友都转发了同一条消息配文出奇一致这次是真的大更新。说实话我一开始也以为只是例行的大版本号推进。Vite 从 2.0 之后3、4、5、6、7 每一代都在优化但说白了都是渐进式改良真正动骨架的次数并不多。直到我把自己负责的两个项目升到 8.0实际跑了几天之后才确认这次确实不一样。如果你正在用 Vite不管你是维护一个几十万行代码的中后台项目还是在维护自己的组件库、文档站这篇文章应该能帮你省下不少摸索时间。我会先讲清楚 Vite 8.0 到底动了哪些底层东西然后给出一份可以直接照做的迁移步骤最后把我这一个月实际踩过的坑、实测到的数据一并放出来。这也是2.0 以来最大更新这个说法的来源上一次出现同级别的变化还是 Vite 2.0 引入 esbuild 做依赖预构建的时候。那一次改变了开发服务器的启动方式这一次改变的是整个打包链路。1. 为什么说这是 2.0 以来最大的更新Vite 自诞生那天起就有个非常讨巧的双引擎设计开发阶段用 esbuild 做依赖预构建把 node_modules 里成千上万的模块预先打包成浏览器能直接加载的 ESM 格式换取极快的冷启动生产构建则交给 Rollup 出正式产物。这个分工在当年是非常聪明的选择esbuild 快是快但产物没有做生产级优化比如代码分割的精细度和 tree-shaking 的深度都不够所以让更成熟的 Rollup 来兜底。1.1 之前的大版本都在改良这次是换心但双引擎方案的成功也埋下了一个长期隐患开发和生产是两套行为。同一个项目开发时跑的是 esbuild 处理过的模块构建时跑的是 Rollup 重新解析的模块。大多数时候二者表现一致可一旦遇到依赖处理边界、钩子执行顺序、模块副作用标记不一致的场景就会出现开发好好的一打包就报错的玄学问题。Vite 3 到 Vite 7 这几代核心工作基本都围绕一件事在不推翻双引擎的前提下做修补。比如 Vite 5 优化了服务器启动时的模块扫描Vite 6 推出了环境 APIEnvironment APIVite 7 把 Rolldown 作为可选项引入构建链路。这些都很重要但没有动到骨架本身。Vite 8.0 做的事情一句话就能讲完把整套打包核心换成了 Rolldown。这是一个用 Rust 写的打包器完全兼容 Rollup 的插件接口同时能以接近 esbuild 的速度完成生产构建。从 8.0 开始Vite 的开发与生产构建终于跑在同一个引擎上此前开发/生产行为不一致的根源被直接拔掉了。1.2 谁最应该关注这次升级如果你的项目属于下面几类我建议重点看后面的迁移章节。第一类是大型中后台项目。这类项目依赖特别多、体积大平时开发冷启动可能十几秒生产构建经常几十秒甚至几分钟。Rolldown 接管后构建耗时的下降是目前所有优化手段里最明显的。第二类是维护组件库、工具库的人。组件库对构建产物格式要求更复杂既要 ESM 又要 CJS有时候还要单独出类型声明。Rolldown 对多格式输出的支持比 Rollup 更顺手构建速度也有明显提升。第三类是 SSR 项目用户。Rolldown 对 SSR 外部化的处理更彻底服务端产物体积和服务启动速度都有改善。至于纯静态营销页、很小的一两个页面站点Vite 8.0 当然也能用但感知不会太强。不用慌升级成本也不高后面会讲。2. Rolldown换掉打包内核到底意味着什么既然最大变化是换引擎我们得先搞清楚 Rolldown 改变了什么。要说明白这个问题得把 Vite 从 2.0 到 8.0 的引擎演化路径简要梳理一下。2.1 从 esbuild 预构建到 Rolldown 预构建Vite 2.0 时代的开发服务器启动时要做两件事一是扫描入口模块二是对依赖做预构建。预构建之前一直由 esbuild 负责它把 CJS 格式的依赖转换成 ESM同时把上千个小模块合并成少数几个大模块从而减少浏览器发起的请求数。Vite 8.0 里预构建已经默认切换到 Rolldown。最直观的感受是预构建产物体积变小了因为 Rolldown 是按依赖的模块图去做合并能处理更复杂的依赖关系而 esbuild 的合并策略相对简单。以我自己一个项目为例预构建产物目录从 160MB 降到了 110MB 左右启动时扫描这些产物文件的时间自然也跟着变短。另一个值得提的细节是重新构建的触发条件。以前依赖预构建的失效判断靠的是追踪 node_modules 相关文件的 mtime偶尔会出现改了依赖但缓存没失效、必须手动删除 node_modules/.vite 才能解决的问题。8.0 里这部分逻辑被重写过同样的情况我复现了三次都没有再出现旧缓存不失效的问题。2.2 为什么 Rust 重写能快这么多聊到这里很多人会问Rust 到底凭什么快这么多我的理解是核心原因不在语言本身而在并行能力和数据结构。Rollup 跑在 JavaScript 单线程环境里模块图建立、AST 转换、tree-shaking 这些环节很难真正并行化。Rolldown 从一开始就按多线程设计模块解析和 Transform 可以分散到多个 worker 里同时跑等到需要合并结果时再统一步骤。另一个关键点是Rolldown 在 Rust 层直接维护模块图和缓存不需要像 Rollup 那样反复在 JavaScript 对象和 AST 之间做序列化转换省掉了大量临时对象分配和垃圾回收。打个不严谨但好理解的比方Rollup 像一个单线程的图书管理员每查一本书都要把书架整个走一遍Rolldown 则是一个多线程的仓库系统每本书都有索引多个管理员可以同时取书而且仓库里直接放的是整理好的数据不需要每次现场加工。2.3 对插件生态的真实影响有人听到换核心第一反应是插件会不会全挂掉。实际上 Rolldown 刻意兼容了 Rollup 的插件 APIVite 主流的插件比如 vitejs/plugin-vue、vitejs/plugin-react、unplugin 系列在 8.0 里都能直接使用这一点不用担心。但 Rolldown 的插件机制确实比 Rollup 灵活。过去在 Rollup 里做 transform、resolve 这类钩子经常要等整个模块图建立到一定程度才能操作Rolldown 允许插件在更早的阶段介入并且支持增量编译。我给自己一个私有插件加了一段 moduleParsed 阶段的转换逻辑跑下来整体兼容性都不错。如果项目里用了比较小众的 Rollup 插件我的建议是先确认它是否依赖了 Rollup 的私有 API。Rolldown 兼容的是公开 API不是内部实现。任何在 hook 里直接访问 bundle 内部对象私有字段的插件都需要适配这个坑后面有具体案例。2.4 一个让我印象深刻的典型案例我在升级后的第三周遇到了一个过去会非常头疼的问题某个第三方库在生产构建时打出来的包引用了它 package.json 里 exports 字段没有声明的子路径。按以前的经验这种问题通常要靠 patch-package 或者插件里专门写 resolve 逻辑来处理。但在 Vite 8.0 里因为开发和构建用的是同一个解析引擎这个问题在开发启动阶段就直接暴露出来了错误提示里会明确告诉你这个子路径没有被 exports 字段暴露。我顺着提示改了一下依赖的引用方式就解决了。放在以前这个问题大概率要等到 CI 构建时才会炸出来然后来回切换版本、加日志排查半天。这种体验层面的改善恰恰是开发与生产行为一致在真实项目里的价值。它不像某个具体功能那么显眼但能帮你省掉大量隐性排查时间。3. Vite 8.0 里值得关注的几个新能力引擎替换是主角但 Vite 8.0 还顺手端上来几道配菜。这几个能力单独看可能不算惊天动地组合在一起体验提升很明显。3.1 Environment API 从实验转为稳定Environment API 是 Vite 6 开始引入的实验特性到了 8.0 已经标记为稳定。它的核心思路是让同一次构建可以针对多个运行环境分别产出而不是像以前那样一个项目要么配 client、要么配 ssr想同时出两份产物还得写两套 config。我举一个实际场景。我们内部有个 BFF 项目前端部分需要在浏览器里跑客户端 bundle又要给 Node 服务端出一份 SSR bundle。以前配置里要区分 build.ssr 和各种条件分支写在同一个配置文件里经常一不留神就互相污染。用了 Environment API 之后可以声明若干个环境client、server甚至还可以加一个给边缘函数用的 edge 环境每个环境有自己的 resolver、插件和 output 配置互不干扰。这个能力对微前端场景同样有价值。多个子应用共享同一套构建配置但希望产出到不同目录或者希望某些子应用用不同的 external都可以通过环境级别的配置解决不用再为每个子应用单独维护一份配置。3.2 内置模块图可视化与分析工具Vite 8.0 里新增了一个让我比较惊喜的地方模块图可视化能力。以前要看某个模块为什么被打进 bundle常规方案是装 rollup-plugin-visualizer输出一个 HTML 分析图。现在 Vite 自己带了类似能力执行构建时加一个参数就能生成模块依赖关系与体积分布的 JSON配合官方 viewer 页面查看。另外 build 阶段还新增了耗时分析输出。我跑构建时加上 profiling 参数结束后终端会直接打印出每个阶段的耗时排序包括 resolve、load、transform、renderChunk 各自花费的时间一眼就能看出瓶颈在哪。以前想拿到这类数据得在插件里手动包一层计时逻辑现在内置了排查依赖解析性能问题会省很多事。3.3 CLI 和配置层面的人性化改进这一代还顺手清理了一批长期标记废弃的 API。启动或构建时报出的错误信息明显更具体了不再是一段笼统的堆栈而是直接告诉你哪个配置项、哪个 hook 用法过时了应该替换成什么。Rolldown 对废弃 API 的警告写得很到位照着提示改就能完成大部分迁移。还有一个很务实的改进配置文件里不再需要为了达到最佳性能去手动折腾一些高级参数。以前我经常要在 optimizeDeps 和 build 之间来回调参比如某些依赖坚持要 include、某些依赖 go 两难。8.0 的默认配置在这些场景下基本都能给出合理结果我自己的项目里几乎没再动过这些选项。4. 从 7.x 迁移到 8.0 的完整实操记录聊完特性直接上干货。下面是我从 Vite 7 迁移到 8.0 的完整过程照做基本不会出问题。4.1 迁移前的三件事第一确认 Node 版本。Vite 8.0 官方要求 Node 20.19 或 22.12。如果你还在用 Node 18这一步就必须先升版本。第二把 package.json 里所有 Vite 相关的包列出来包括 vite、vitejs/plugin-vue、vitejs/plugin-react、各种 vite-plugin-*逐一检查它们的 peerDependencies 是否支持 Vite 8。第三清理构建机或本地环境里可能存在的全局缓存我建议直接删掉 node_modules 和 lock 文件里的旧版本锁定重新安装避免把旧引擎的残留文件带进来。这里有一条我个人的经验迁移前先把当前项目在旧版本下的构建时长和产物体积记录一下作为升级后的对比基线。否则升级完你会觉得快了但说不清快了多少后续想写评估报告或者给团队汇报都缺乏数据支撑。4.2 升级命令与配置调整如果项目用的是 pnpm执行pnpm add -D vite^8.0.0 pnpm add -D vitejs/plugin-vuelatest pnpm installnpm 或者 yarn 也类似把包管理器对应命令的版本号锁定到 8.x 即可。装完之后先别急着启动直接跑一次构建看会不会报错。需要留意的是几个被移除的旧选项。比如 build.terserOptions 相关的接口以前如果你在这里配过压缩参数现在要改成使用 rollup 原生的代码压缩插件形式还有 optimizeDeps.keepNames 之类的旧选项在 8.0 里已经不存在直接在配置里删掉。启动 dev 或 build 时如果报某个配置项不识别大概率就是命中了这类废弃项搜一下新版的名字替换就行。4.3 插件适配清单我自己整理了一份兼容性速查可以对照着看插件类型典型代表8.0 兼容情况框架插件vitejs/plugin-vue、vitejs/plugin-react直接兼容建议升级到最新版按需自动导入unplugin-auto-import、unplugin-vue-components兼容注意升级到支持 Rolldown 的版本分析工具rollup-plugin-visualizer兼容但 Vite 8 已内置可视化可逐步替代老牌 Rollup 插件rollup/plugin-* 系列大部分兼容小心依赖私有 API 的插件自定义内部插件自己维护的需要逐个检查是否触碰内部实现我给过一个判断标准插件代码里如果出现了 bundle.xxx 这种直接操作内部对象的写法升级后大概率要改。正确的做法是改用 this.getModuleInfo、this.emitFile 这类公开 API。4.4 迁移后的第一轮验证配置改完、构建通过之后不要急着上线先按下面的顺序验证一遍先跑一次完整的 production build检查产物有没有缺模块、警告有没有异常然后启动 dev server重点看冷启动时间、页面路由跳转和 HMR 表现接着把 SSR 场景单独打一遍确认服务端产物正常。如果以上都过了再跑一遍团队现有的端到端测试。我的项目里E2E 测试是最后兜底的一环前后端联调场景覆盖得比较全构建层面出了问题基本能在这一轮暴露出来。整套验证下来半天时间是足够的。5. 我踩过的坑迁移过程中值得记录的问题官方的迁移文档写得再清楚也覆盖不了所有真实场景。下面这几个问题是迁移过程中我自己踩到、并且花了时间排查的记录下来给大家做参考。5.1 依赖预构建缓存导致的样式错乱第一次升级后我贪图省事直接沿用了 node_modules/.vite 缓存目录结果 dev server 启动后页面加载出来的样式错乱。排查后发现旧的预构建产物是 esbuild 生成的格式与 Rolldown 的产物混在一起了。解决办法很直接删掉 node_modules/.vite 重新启动。之后我又试了几次其实 Vite 8.0 检测到预构建实现变化时本身会触发重建但如果你手动改过依赖版本、又保留了缓存还是建议主动删一次避免意外。这个问题的教训是大版本升级后第一次启动前先清理缓存目录这是成本最低的预防动作。别省这一步。5.2 自定义插件在 Rolldown 下丢失 transform 结果有个内部插件负责给特定模块注入全局变量声明在 Rollup 下工作正常到 Rolldown 下偶尔不生效、时好时坏。排查了很久才发现插件里用了强制同步的 fs.readFileSync 去读一个运行时才生成的文件时序和 Rolldown 的并行 transform 冲突了。改成在 buildStart 阶段预读内容把结果缓存进闭包变量问题就消失了。这条经验很重要如果你的插件里做了 IO 操作尽量把 IO 前置到插件生命周期早期不要在 transform 钩子里频繁读文件。Rolldown 的解析是并行的IO 密集操作会拖慢构建还容易产生不可预期的时序问题。5.3 SSR external 的默认行为变化另一个 SSR 项目升级后发现服务端产物里被打进了不少本来应该 external 的 Node 内置模块和依赖。原因是 Vite 8.0 对 ssr.external 的默认判断逻辑改了以前需要手动在列表里列出的包现在会根据 package.json 的 type 字段自动判断但个别老包没有正确标记于是被打进了产物。解决办法是把这些包显式加回 ssr.external 配置同时在 ssr.noExternal 里放行那些确实需要被打包的 ESM-only 依赖。如果你在升级后看到X cannot be resolved或者产物体积突然变大优先怀疑这个方向。5.4 一个老插件卡在 Rollup 私有 API 上我们项目里有一个用了两年的老插件直接读取了 Rollup 内部模块图的私有数据结构升级后在 Rolldown 环境下运行行为异常。处理过程比较费劲因为文档里不会写这种用法。最终的解决路径是先在社区 GitHub issues 里搜这个插件的名字看有没有人提交过兼容 Roldown 的 issue如果有就等更新没有就必须自己改。我们把所有对私有字段的访问全部换成公开 API 之后插件恢复正常。这类问题没有银弹唯一能给你的建议是私有 API 在迁移时是最大的不确定因素提前盘点项目里所有自定义插件给自己的代码多留出半天缓冲时间。6. 实测数据与场景化选型建议最后用数据说话我把三个不同项目的迁移前后对比列出来给大家一个更直观的判断依据。项目类型代码规模Vite 7 构建耗时Vite 8 构建耗时冷启动提升中后台管理端约 12 万行业务代码41s12s9s 到 3s组件库86 个组件24s6.5s5s 到 2s文档站SSR约 300 个 MDX 页面32s9s6.5s 到 2.5s可以看到收益是全场景的。组件库场景里Rolldown 对多格式输出的支持明显更好以前为了同时出 ESM、CJS、MJS 几种格式要做额外配置现在基本零成本。SSR 项目里服务端产物体积也小幅下降服务启动时间跟着缩短。至于要不要升级我的判断是如果项目已经在用 Vite 7且没有深度依赖 Rollup 私有 API 的插件升级收益远大于风险整个过程半天足够。如果还在 Vite 5 之前的老版本建议先升到 7熟悉一下 Environment API 的结构再平滑过渡到 8。这一代改的东西确实多一步跨三个版本配置文件的改动面会大不少。如果项目里还在用 webpack并且体量不大暂时没必要为了追新而迁移。Vite 8.0 的收益要在依赖多、构建链路复杂的项目里体现得最充分小项目换过去体验差距不明显反而要承担迁移成本。我自己实际用了一个月之后的感受是工具链升级只是起点整个产线随之重新平衡的过程才是真正花时间的地方。升级完成后的那一周我去看了 CI 流水线的耗时分布发现 Vite 构建环节从 40 秒降到 12 秒之后镜构建和产物压缩反而成了新的瓶颈。一开始我还怀疑是不是哪里配置出了问题后来想明白是之前的流水线一直在等构建这个慢环节现在构建快了下游环节的相对占比自然就上来了。所以最后分享一个小建议升级之后别急着高兴先把 CI 流水线每阶段的耗时记录拉出来看一圈把优化重点放到真正占时间的那一步上。这次的迁移给我的体会是换引擎解决的是最痛的那个点但整个系统是联动的后续还有很多地方值得跟着重新调一遍。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →