Vite 8 迁移:CJS 默认导入、manualChunks 与 commonjsOptions 的三处硬坑
Vite 8(2026-03-12)把生产打包换成 Rolldown(Rust),依赖预打包也从 esbuild 换成 Rolldown,JS transform/minify 交给 Oxc,CSS 压缩交给 Lightning CSS,产物为 ESM only。安装体积会多出约 15MB 原生二进制。
多数配置能用自动转换顶过去,真正的麻烦集中在下面几处:它们要么静默改变语义,要么直接让构建失败。
- 迁移指南:https://vite.dev/guide/migration
- 发布说明:https://vite.dev/blog/announcing-vite8
- 升级踩坑记录:https://ortamarco.me/en/blog/what-breaks-upgrading-to-vite-8/
1. CJS default 导入规则变了,而且不报构建错误
新规则:default 导入等于 module.exports 整体,当且仅当满足下列任一条件:
- importer 是
.mjs/.mts - importer 最近的
package.json有"type": "module" - 被导入的 CJS 模块的
module.exports.__esModule不为true
否则 default 等于 module.exports.default。
旧版本的问题在于 dev 和 build 行为不一致:dev 只在依赖优化阶段对 .mjs / "type": "module" 生效;build 侧看的是 __esModule 和 default 属性是否存在,默认 commonjsOptions.defaultIsModuleExports = 'auto'。
典型症状:某个包导出 module.exports = { __esModule: true, default: function greet(){} },在普通 .js(无 type: module)里写 import greet from 'cjs-pkg'; greet(),Vite 7 的 dev 能跑,Vite 8 抛 TypeError: greet is not a function——拿到的是整个 module.exports,函数藏在 .default 里。
构建过程不会提示这个变化。
逃生开关已被标记弃用:
export default defineConfig({
legacy: { inconsistentCjsInterop: true },
})
先确认实际拿到的是什么:
node -e "const m=require('cjs-pkg'); console.log(Object.keys(m), m.__esModule, typeof m.default)"
2. output.manualChunks 的对象形式被移除
不是弃用,是移除。对象写法直接构建失败:
// 旧:Vite 8 直接失败
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
charts: ['echarts'],
},
},
},
}
函数形式仍可用,但已弃用,官方指向 Rolldown 的 codeSplitting 选项。同一批改动里:
build.rollupOptions→build.rolldownOptions(弃用但自动转换,构建时有弃用警告)worker.rollupOptions→worker.rolldownOptionsbuild.rollupOptions.watch.chokidar被移除,改用build.rolldownOptions.watch.watcher
3. build.commonjsOptions 成了 no-op
没有警告,没有错误,直接忽略。之前用 include / exclude / requireReturnsDefault 兜某个 CJS 包的 workaround 会静默失效——包还在,配置没报错,行为已经不一样了。
# 一次扫出三类残留
rg -n "rollupOptions|commonjsOptions|esbuildOptions|transformWithEsbuild" \
--glob '!node_modules' --glob '!dist'
4. 默认 target 抬升与 Node 门槛
默认 build.target 变成 Chrome 111 / Edge 111 / Firefox 114 / Safari 16.4。如果项目原本靠默认 target 覆盖更老的浏览器,需要显式写回 target,否则低版本环境直接白屏,而构建一切正常。
Node 门槛是 20.19+ / 22.12+,和 Vite 7 相同(需要 require(esm)),从 Vite 7 升上来的项目这一项通常不用动。
配置对照
| 旧写法 | Vite 8 |
|---|---|
build.rollupOptions | build.rolldownOptions(自动转换 + 弃用警告) |
worker.rollupOptions | worker.rolldownOptions |
output.manualChunks: { ... } | 移除,改函数形式或 codeSplitting |
build.commonjsOptions | no-op,静默忽略 |
rollupOptions.watch.chokidar | rolldownOptions.watch.watcher |
optimizeDeps.esbuildOptions | optimizeDeps.rolldownOptions(弃用,自动转换) |
esbuild.jsxInject | oxc.jsxInject |
esbuild.jsxFactory | oxc.pragma |
optimizeDeps.esbuildOptions 的字段映射:minify → output.minify、treeShaking → treeshake、define → transform.define、loader → moduleTypes。
其余需要顺手过的点
- esbuild 不再是直接依赖,用了
transformWithEsbuild的插件要自己装 esbuild。 - Oxc Minifier 是默认 minifier;
build.minify: 'esbuild'可回退(需装 esbuild)。mangleProps这类属性混淆不支持。 - Lightning CSS 默认做 CSS 压缩,CSS 包体可能略增。
- 移除
module/browser字段的格式嗅探,严格按resolve.mainFields顺序解析。 - 外部化模块的
require调用保留为require,需要转换时用esmExternalRequirePlugin。 - JS API 的
build()改为抛BundleError,错误在e.errors数组里。 - 插件侧:dev 下不再调用
moduleParsedhook;esbuild 自有onLoad/onResolve格式的插件不被支持。 rolldown-vite是 Vite 7 + Rolldown 的过渡包,不是 Vite 8 的替代品。