代码 Vite 8 迁移:CJS 默认导入、manualChunks 与 commonjsOptions 的三处硬坑

2026-11-08 21:05:22

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.rolldownOptions
  • build.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.rollupOptionsbuild.rolldownOptions(自动转换 + 弃用警告)
worker.rollupOptionsworker.rolldownOptions
output.manualChunks: { ... }移除,改函数形式或 codeSplitting
build.commonjsOptionsno-op,静默忽略
rollupOptions.watch.chokidarrolldownOptions.watch.watcher
optimizeDeps.esbuildOptionsoptimizeDeps.rolldownOptions(弃用,自动转换)
esbuild.jsxInjectoxc.jsxInject
esbuild.jsxFactoryoxc.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 下不再调用 moduleParsed hook;esbuild 自有 onLoad / onResolve 格式的插件不被支持。
  • rolldown-vite 是 Vite 7 + Rolldown 的过渡包,不是 Vite 8 的替代品。
复制全文 生成海报 Vue Vite 构建工具

推荐文章

程序员茄子在线接单