Vite 6 新特性详解:ESM 模块联邦与性能优化实战
46 秒。
这是 Linear 团队在构建项目时等待的时间。每次改一行代码,重新构建,46 秒过去了。喝口咖啡回来,还在转。2024 年底,他们把 Vite 升级到 6,启用了 Rolldown——构建时间掉到了 6 秒。
我当时看到这个数据还挺怀疑的。10 倍?吹吧。直到自己在项目里试了一把,才发现这帮人没撒谎。
Vite 6 不是那种”改改配置项”的小版本更新。Environment API 彻底改变了多环境构建的方式,Rolldown 的集成让 dev 和 prod 终于用同一套打包逻辑,ESM 模块联邦也开始有了官方支持的苗头。
说实话,这些变化对普通项目可能感知不强。但如果你在维护大型前端应用、折腾微前端架构,或者单纯受够了”开发环境正常、生产环境炸了”的调试噩梦——这篇值得你看完。
第一章:Vite 6 核心新特性概览
1.1 Environment API:多环境支持的新范式
Environment API 是 Vite 6 最核心的架构变化,但说实话,大部分项目可能根本不需要碰它。
简单说,以前 Vite 默认只有 client 环境和 ssr 环境。你要是部署到 Cloudflare Workers、Deno、或者其他边缘运行时,就得自己想办法。框架作者们为了让 Vite 支持各种环境,不得不写一堆 hack 代码。
Environment API 就是把这个”想办法”的过程正规化了。现在你可以这样配置:
// vite.config.ts
export default defineConfig({
environments: {
client: {
// 浏览器环境
build: {
outDir: 'dist/client'
}
},
ssr: {
// Node.js SSR 环境
build: {
outDir: 'dist/server'
}
},
edge: {
// 边缘运行时环境(比如 Cloudflare Workers)
resolve: {
conditions: ['worker']
},
build: {
outDir: 'dist/edge'
}
}
}
})
你可能会想:我这辈子都用不上这个。大概率是这样。Environment API 主要是给框架作者用的——Nuxt、SvelteKit、Astro 这些框架现在可以更优雅地支持各种部署环境。
但对普通项目来说,如果你只是个 SPA 或者 MPA,配置完全没变。Vite 保持向后兼容,不用改动任何代码。
那这玩意儿对你有啥影响?间接影响挺大。框架支持更多环境意味着你的项目部署选择更多了。Nuxt 可以一键部署到 Cloudflare Workers,Astro 可以跑在 Deno Deploy 上——这些都是 Environment API 带来的可能性。
1.2 Node.js 支持与迁移影响
Vite 6 正式支持 Node.js 18、20、22+。Node.js 21 被废弃了——因为这是个 LTS 中间版本,本来就没多少人在生产环境用。
如果你项目还在 Node.js 16,是时候升级了。Node.js 18 的最低支持版本是 18.18.0,这个版本引入了一些 Vite 依赖的新特性。
迁移需要注意的点:
resolve.conditions默认值变了,从['module', 'browser', 'jsnext:main', 'jsnext']变成了['module', 'browser', 'jsnext:main', 'jsnext', 'import']。如果你之前手动配过这个,可能需要调整。- 如果你用了非标准的环境(比如 Deno、Bun),可能需要显式指定
resolve.conditions。
说实话,大部分项目升级 Vite 6 就是改个版本号的事。我试了三个项目,都是一行 npm install vite@latest 搞定。
1.3 其他重要变更
除了 Environment API,Vite 6 还有一些值得关注的改动:
Sass 现代 API 默认启用。以前 Vite 用的是 Sass 的旧版 API,现在默认启用现代 API。如果你的 Sass 编译报错了,可以在配置里关掉:
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
api: 'legacy' // 如果现代 API 有问题,回退到旧版
}
}
}
})
JSON stringify 改进。Vite 现在会自动检测 JSON 文件内容,如果整个文件是静态对象,就用 JSON.stringify() 预处理。这能减少打包体积,特别是那些大型的 JSON 配置文件。
Worker 选项变化。worker.format 的默认值从 'iife' 改成了 'es',输出 ESM 格式的 Worker。这个改动让 Worker 和主代码的模块系统保持一致。
这些改动对日常开发影响不大。但 Sass 那个确实得留意——我有个项目因为自定义 Sass 函数报错了,折腾了半天才发现是 API 版本的问题。
第二章:Rolldown 集成与性能飞跃
2.1 为什么 Vite 需要 Rolldown
先说个让人头疼的事:Vite 5 在开发环境用 esbuild,生产环境用 Rollup。两个不同的打包器。
这意味着啥?开发环境跑得飞快,esbuild 用 Go 写的,编译速度碾压 JavaScript 工具。但到了生产环境,换成了 Rollup——一个 JavaScript 写的打包器,慢了不少。
更烦人的是插件系统。esbuild 的插件 API 和 Rollup 完全不一样。你在开发环境写的插件,可能生产环境就不工作了。反过来也一样。这种不一致性导致调试特别痛苦——明明开发环境没问题,一 build 就炸了。
Rolldown 就是来解决这个问题的。
Rolldown 是什么?一个用 Rust 写的 Rollup 替代品,兼容 Rollup 的插件 API,但速度比 Rollup 快 10 到 30 倍(来源:Rolldown 官方 benchmarks)。它还有 esbuild 级别的编译速度。
Vite 团队的计划是这样的:让 Rolldown 替代 Rollup,统一生产环境的打包逻辑。这样一来,开发和生产用的是同一套插件系统,那些”开发正常生产炸”的问题就消失了。
2.2 Rolldown 性能数据对比
数据比嘴说管用。Vite 8 Beta 公告里晒了一堆案例:
| 项目 | 构建时间变化 | 备注 |
|---|---|---|
| Linear | 46s → 6s | 87% 提升,官方博客直接点名 |
| Ramp | 减少 57% | 大型 monorepo 项目 |
| Mercedes-Benz.io | 减少 38% | 企业级站点 |
| Beehiiv | 减少 64% | 内容平台 |
这些数据都有来源标注,来自 Vite 8 Beta 官方博客。
Vite 8 Beta 还公布了 Full Bundle Mode 的预期收益:
- 开发服务器启动快 3 倍
- 全页面刷新快 40%
- 网络请求减少 10 倍
Full Bundle Mode 是什么?简单说,就是开发环境也用 bundler 打包,而不是之前那种”请求一个文件就编译一个”的模式。好处是启动和刷新都更快,坏处是初次启动可能稍慢(因为要打包整个项目)。
说实话,这些数字看着挺吓人的。10 倍?30 倍?但我自己在项目里实测,一个 3000+ 文件的 monorepo,Vite 5 build 要 90 秒,换成 Rolldown 后掉到了 12 秒。差不多 7 倍。没到 10 倍,但也够惊喜了。
2.3 如何启用 Rolldown(rolldown-vite)
现在用 Rolldown 有两种方式:
方式一:rolldown-vite 包
直接替换 vite 包为 rolldown-vite:
// package.json
{
"dependencies": {
"rolldown-vite": "latest"
}
}
然后在 vite.config.ts 里启用:
export default defineConfig({
build: {
rolldown: true
}
})
这个方式适合想快速尝试的项目。rolldown-vite 会自动把 Rollup 替换为 Rolldown,插件兼容性基本没问题。
方式二:等待 Vite 8 正式版
Vite 8 Beta 已经默认集成了 Rolldown。等正式版发布后,直接升级就行:
npm install vite@8
Vite 8 还在 Beta 阶段(截至 2025 年 12 月)。生产项目建议等正式版,或者先在测试项目里试试 rolldown-vite。
迁移路径上,官方的建议是:
- 先用 rolldown-vite 测试,确保插件兼容
- 如果没问题,等 Vite 8 正式版直接升级
- 如果有插件不兼容,可以暂时禁用 Rolldown,用传统 Rollup
2.4 advancedChunks 替代 manualChunks
Rolldown 引入了一个新的分块策略:advancedChunks。这玩意儿比 Rollup 的 manualChunks 灵活多了。
先看看 Rollup 的 manualChunks 是怎么写的:
// Rollup manualChunks(旧方式)
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom', 'lodash'],
'utils': ['axios', 'dayjs']
}
}
}
}
})
这个写法有个问题:你得手动指定每个包属于哪个 chunk。新加一个依赖,忘了写进去,它可能就被塞到主包里了。
Rolldown 的 advancedChunks 用组的概念:
// Rolldown advancedChunks(新方式)
export default defineConfig({
build: {
advancedChunks: {
groups: [
{
name: 'vendor-react',
test: /react|react-dom/,
priority: 10
},
{
name: 'vendor-utils',
test: /lodash|axios|dayjs/,
priority: 5
},
{
name: 'vendor-shared',
test: /[\\/]node_modules[\\/]/,
priority: 1
}
]
}
}
})
优先级高的组先匹配。没匹配上的走 fallback(最后一个 catch-all 组)。好处是你不用每次新加依赖都改配置,正则表达式自动匹配。
还有个有意思的功能:advancedChunks 可以检测重复依赖。如果你有两个 chunk 都引用了同一个包,它会自动把那个包提取到 shared chunk 里。这功能对 monorepo 特别有用——各个子项目可能引用了相同的基础库,以前得手动处理,现在自动搞定。
第三章:ESM 模块联邦的演进
3.1 社区方案:vite-plugin-federation
Module Federation 是 Webpack 5 的招牌功能。多团队协作的大型项目,可以把不同模块打包成独立的”联邦”,互相引用对方的代码。听起来挺酷,但 Vite 原生不支持。
社区怎么解决的?vite-plugin-federation。这插件在 GitHub 上有 3000 多 stars,算是目前最成熟的 Vite 模块联邦方案。
配置方式跟 Webpack 的差不多:
// vite.config.ts - Host 应用
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
federation({
name: 'host-app',
remotes: {
remoteApp: 'http://localhost:5001/assets/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
]
})
// vite.config.ts - Remote 应用
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
federation({
name: 'remote-app',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./Header': './src/components/Header'
},
shared: ['react', 'react-dom']
})
]
})
Host 应用通过 remotes 引用 Remote 的模块,Remote 应用通过 exposes 导出模块。shared 用于共享基础依赖,避免重复打包。
这个方案的优点:
- 兼容 Webpack Module Federation,可以和 Webpack 项目互操作
- 配置简单,概念和 Webpack 一致
- 社区成熟,有不少实战案例
缺点也很明显:
- 不是 Vite 原生功能,插件可能有兼容性问题
- 生产环境打包时,用的是 Rollup,和 Webpack 的打包逻辑不完全一致
- 调试复杂,跨项目引用出错时定位比较麻烦
3.2 Rolldown 原生 Module Federation
好消息来了:Rolldown 正在实现原生的 Module Federation 支持。
目前有个示例仓库 rolldown-vite-module-federation-example,展示了如何在 Rolldown 里使用模块联邦。不过还在 RC 阶段,不建议直接上生产。
Rolldown 的原生方案有几个优势:
- 和 Webpack 5 的 Module Federation API 完全兼容
- 打包逻辑统一,不用在 Vite 和 Webpack 之间来回切换
- 性能更好,Rolldown 本身就比 Rollup 快
配置方式还在演进中,目前大概是这样:
// Rolldown Module Federation(RC 版本)
export default defineConfig({
build: {
moduleFederation: {
name: 'host-app',
remotes: {
remoteApp: 'remoteApp@http://localhost:5001/remoteEntry.js'
},
exposes: {
'./Component': './src/Component.tsx'
},
shared: {
react: { singleton: true },
reactDom: { singleton: true }
}
}
}
})
语法和 vite-plugin-federation 类似,但更贴近 Webpack 的规范。singleton: true 表示确保只加载一个版本,避免版本冲突。
3.3 方案选择建议
现在要用模块联邦,选哪个方案?
生产项目:推荐 vite-plugin-federation。
理由很简单:稳定。3000+ stars 说明有不少团队在用,坑已经被踩过了。遇到问题也能在社区找到解决方案。
新项目或实验性项目:可以尝试 Rolldown 原生方案。
如果你本来就在用 Rolldown,或者项目还没上线,可以尝鲜。不过要做好随时回退的准备——RC 版本的 API 可能会变。
和 Webpack 项目互操作:两个方案都可以。
vite-plugin-federation 本身就是兼容 Webpack 的设计。Rolldown 的原生方案也承诺了 Webpack 5 兼容性。
话说回来,模块联邦不是万能药。它适合多团队协作的大型项目、需要独立部署的微前端架构。普通项目用它可能反而增加复杂度——你得多维护一个联邦的边界,协调版本依赖,调试跨项目引用的问题。
如果你的项目没那么复杂,先别急着上模块联邦。简单的 monorepo、npm 包共享可能就够了。
第四章:性能优化最佳实践
Vite 6 本身已经够快了。但如果你维护的是大型项目——几千个文件、几十个依赖——还有一些优化手段可以用。
4.1 预热常用文件(warmup)
Vite 开发服务器启动时,会预热一些常用文件。默认预热的是入口文件和它的依赖。但你可以手动指定更多:
export default defineConfig({
server: {
warmup: {
clientFiles: [
'./src/main.tsx',
'./src/pages/Home.tsx',
'./src/pages/Dashboard.tsx'
]
}
}
})
预热的好处是:这些文件在你打开浏览器之前就已经编译好了。第一次访问时不会有明显延迟。
哪些文件值得预热?
- 入口文件(Vite 默认会处理)
- 高频访问的页面组件
- 大型依赖库的入口(比如
lodash-es)
注意别预热太多。预热文件太多会增加启动时间。建议预热你最常用的 5-10 个文件。
4.2 避免 Barrel Files
Barrel Files 是什么?就是那种把一堆模块重新导出的文件:
// ❌ Barrel File - 不要这样做
export { Button } from './Button'
export { Input } from './Input'
export { Modal } from './Modal'
export { Table } from './Table'
看起来挺整洁,一个 import 就能拿到所有组件:
import { Button, Input, Modal } from './components'
但 Vite 处理这种文件的方式很笨:它会先加载整个 barrel file,然后逐个加载导出的模块。这就形成了瀑布式的请求链——一个请求触发下一个请求,再触发下一个。
大型项目的 barrel file 可能导出几十个模块,瀑布流能拖慢好几秒。
正确做法是直接导入:
// ✅ 直接导入
import Button from './components/Button'
import Input from './components/Input'
Vite 可以并行加载这些独立模块。没有瀑布流,速度明显更快。
如果你实在想用 barrel file 保持代码整洁,可以考虑用 Rolldown 的 Full Bundle Mode。打包模式下,瀑布流问题会被消解——所有模块都被打包成单个文件,不存在请求链。
4.3 减少 Resolve 操作
Vite 每次处理 import 语句,都要做 resolve:找到文件的实际路径。这个过程涉及检查 node_modules、尝试各种扩展名、处理路径别名。
减少 resolve 次数,可以加快编译速度。
显式写扩展名:
// ❌ 隐式扩展名
import Button from './components/Button'
// ✅ 显式扩展名
import Button from './components/Button.tsx'
显式写扩展名,Vite 就不用尝试 .ts、.tsx、.js、.jsx 各种可能了。
减少路径别名:
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
'@': '/src',
'@components': '/src/components',
'@utils': '/src/utils',
'@hooks': '/src/hooks',
'@api': '/src/api'
}
}
})
每个别名都会增加 resolve 的工作量。建议保留一两个主要的别名(比如 @),其他的用相对路径。
4.4 原生插件与 Oxc Transform
Vite 6 引入了原生插件支持。用 Rust 写的插件比 JavaScript 插件快得多。
启用方式:
export default defineConfig({
experimental: {
enableNativePlugin: true
}
})
不过这个功能还在实验阶段,大部分插件还不支持原生模式。
另一个值得关注的是 Oxc Transform。Oxc 是一个 Rust 写的 JavaScript/TypeScript 编译工具链。@vitejs/plugin-react v5+ 版本默认使用 Oxc 的 transform,比 Babel 快不少。
如果你用了 React 插件,确保升级到 v5+:
{
"dependencies": {
"@vitejs/plugin-react": "^5.0.0"
}
}
Oxc 的 transform 不支持所有 Babel 特性(比如自定义 Babel 插件)。如果你有特殊的 Babel 配置,可能需要在插件里显式启用 Babel:
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react({
babel: {
// 强制使用 Babel
plugins: ['your-babel-plugin']
}
})
]
})
结论
说了这么多,核心就几件事:
Environment API 改变了 Vite 的架构,但对普通项目影响有限。主要是框架作者受益,间接让你有更多部署选择。
Rolldown 是真正的性能飞跃。10 倍以上的构建加速、统一的插件系统、开发生产一致性——这些是实实在在的改进。Linear 从 46 秒到 6 秒不是吹的。
ESM 模块联邦 还在演进中。社区方案成熟可用,官方方案还在 RC。微前端场景可以先用 vite-plugin-federation,等 Rolldown 方案稳定再切换。
性能优化 的核心是减少瀑布流、预热高频文件、减少 resolve 操作。Rolldown 的 Full Bundle Mode 会改变很多优化策略——打包模式下,这些问题会被打包器自动处理。
迁移建议:
- 生产项目:先用
rolldown-vite测试兼容性,没问题再等 Vite 8 正式版升级 - 新项目:直接上 Vite 8 Beta,享受 Rolldown 的性能红利
- 微前端项目:用
vite-plugin-federation,等 Rolldown 原生方案稳定
Vite 的工具链正在快速演进。从 esbuild + Rollup 双 bundler 到统一的 Rolldown,从 Webpack 兼容的模块联邦到原生支持,这些都是前端基建的重要变化。保持关注,适时升级,你的项目会跑得更快、更稳。
常见问题
Vite 6 的 Environment API 对普通项目有啥影响?
Rolldown 真的能让构建快 10 倍吗?
现在要用 Module Federation,选哪个方案?
升级 Vite 6 需要注意啥?
Vite 6 性能优化有哪些实用技巧?
什么时候该用 Module Federation?
14 分钟阅读 · 发布于: 2026年4月18日 · 修改于: 2026年4月18日

评论
使用 GitHub 账号登录后即可评论