公司那套内部系统原先是 Vue CLI 4 搭的,Webpack 4。五月份花了一个下午换成 Vite 6,本地跑起来那一刻还挺高兴,dev server 冷启动从四十多秒降到两三秒。然后补坑补了三天,全是「本地好好的、打包之后才出事」这类问题。这里记三个。
说明一下,这个项目不大,四十来个页面,用的 Vue 3 + TypeScript,部署在公司内网的一个子路径下。我不是专业前端,下面的解释如果有更准确的说法,欢迎写信纠正。
坑一:环境变量的加载规则完全不一样
Webpack 那套(Vue CLI)里我们写的是 process.env.VUE_APP_API_BASE,变量定义在 .env 文件里,前缀必须是 VUE_APP_。Vite 换成了 import.meta.env.VITE_API_BASE,前缀必须是 VITE_。这些迁移文档里都写了,我照着改完,本地一跑全是 undefined/api/... 的 404。
原因有两个,都是我自己没看仔细:
- 文件位置:Vite 只从
root指定的目录(默认是项目根,也就是 vite.config.ts 所在那层)加载.env。我沿用了老习惯把 env 文件放在src下面,一个都没读进去。 - 改完要重启:dev server 启动时才读 env 文件,改完不重启不生效。这一点在 Webpack 那边其实也差不多,但我当时以为热更新会带上。
Vite 加载的顺序是 .env → .env.local → .env.[mode] → .env.[mode].local,后面的覆盖前面的。还有个坑是不满足前缀的变量不会报错,只会是 undefined,所以上线前最好自己打一行日志确认一下。
过渡期我在配置里加了 envPrefix,让两种前缀都生效,等代码全改完再删掉:
// vite.config.ts
export default defineConfig({
envPrefix: ['VITE_', 'VUE_APP_'],
// ...
})
顺带提一句,process.env.NODE_ENV 在 Vite 里也还能用,但只有 dev / production 两个值,想判断别的模式要用 import.meta.env.MODE。
坑二:CommonJS 依赖要手动处理
dev 阶段 Vite 用 esbuild 做依赖预构建,大部分 CommonJS 包能被转成 ESM,所以本地一切正常。到 vite build 换成 Rollup 打包时就露馅了。我们遇到两个:
一个是公司自己打的一个内部组件库,umd 格式的,构建时报 [commonjs--resolver] Failed to resolve entry for package "@internal/ui-kit"。它 package.json 里的 module 字段指向的文件其实不存在,只有 main。最后是把它加进预构建白名单,并让 commonjs 插件处理它:
export default defineConfig({
optimizeDeps: {
include: ['@internal/ui-kit'],
},
build: {
commonjsOptions: {
include: [/@internal[\\/]ui-kit/, /node_modules/],
transformMixedEsModules: true,
},
},
})
另一个更典型:有个老模块里写了动态 require,形如 require(`./locales/${lang}.js`)。Rollup 是静态分析,处理不了这种拼接,构建能过但运行到那儿就报 require is not defined。改法是换成 Vite 的 import.meta.glob 先静态收集:
const modules = import.meta.glob('./locales/*.js')
async function loadLocale(lang) {
const load = modules[`./locales/${lang}.js`]
return load ? await load() : null
}
这个改动不大,但那两天的教训是:dev 能跑不代表能构建。后来我把「先在本地跑一遍 vite build 再用 vite preview 打开点几下」加进了发布流程,就这一步挡掉了后面两次类似的问题。
坑三:只在打包之后才冒出来的路径问题
这个是我到现在也没完全想明白的。我们的系统部署在内网的 /ops/ 子路径下,dev 下打开一切正常,打包部署之后白屏,控制台是 assets/index-a3f2c1.js 404。
第一个原因很清楚:Vite 的 base 默认是 '/',生成的资源引用是绝对路径 /assets/...,在子路径下自然找不到。加上 base: '/ops/' 之后主资源好了。同时 history 模式的路由刷新会 404,这个在 nginx 上加 try_files $uri $uri/ /ops/index.html; 解决,跟 Vite 本身没关系。
麻烦的是后面两处。一处是动态 import 的路由:老代码里有 import(`@/views/${name}.vue`) 这种拼接写法,构建时 Vite 只是给了一句警告,说这个动态导入无法静态分析,产物里就真没生成对应的 chunk,用户点到那个菜单才报 Failed to fetch dynamically imported module。最后改成用 import.meta.glob 建一张显式的映射表才解决。
另一处是相对路径 base。我试过把 base 写成 './',在 /ops/ 下打开是正常的,但进到二级路由 /ops/report/detail 再刷新,资源路径就变成 /ops/report/assets/...,又坏了。最后还是回到 base: '/ops/' 加服务端配合。
我没想明白的是:相对路径 base 在多层路由下到底应该怎么正确工作,是我 nginx 配得不对,还是它本来就不适合配 history 路由。试了三种组合之后我先用了能跑的那个,没有继续深挖,所以这里只能算记录,不算结论。
小结
三个坑里,前两个属于「改了配置就完事」,第三个我到现在心里没底。唯一的通用经验是上面那句:别只看 dev。迁移本身确实很快,省下来的时间基本都花在验证上了。
(2026-07 补充:这套配置到现在跑了两个月,没有再出新的路径问题。倒是有一次同事本地构建报内存不足,给 NODE_OPTIONS=--max-old-space-size=4096 就好了,跟本文无关,顺手记一下。)