拾光志

把一个老项目迁到 Vite 之后

2026-02-12 · 拾光前端Vite

接手了一个 2019 年起的老项目,webpack 4 + Vue 2,冷启动要 40 多秒,改一行代码热更新也得等三四秒。年前终于下定决心迁到 Vite,折腾了两个周末,现在冷启动不到两秒,热更新基本是即时的。这篇把踩过的坑记一下,给后来人少走点弯路。

为什么是 Vite 而不是升级 webpack

也考虑过升 webpack 5,但算了一下收益——主要就是持久化缓存能快点——对比迁移成本其实不划算。Vite 的开发体验是量变到质变的,dev server 用原生 ESM,几乎不打包,启动快得不像话。而且 Vite 对 Vue 的支持是一等公民,迁移阻力最小。

坑一:动态 import 的路径

webpack 支持用变量做动态 import,比如 import('./pages/' + name),它会帮你把整个目录打包进去。但 Vite 基于 ESM,用的是 import.meta.glob,写法完全不一样:

// webpack 写法(Vite 下不工作)
const mod = await import('./pages/' + name)

// Vite 写法
const modules = import.meta.glob('./pages/*.vue')
const mod = await modules['./pages/' + name + '.vue']()

项目里这种动态加载有十几处,全得手改。好在模式比较统一,写了个脚本批量替换。建议迁移前先全局搜一下 import(,心里有个数。

坑二:环境变量前缀

webpack 里 process.env.ANY_VAR 都能读到,Vite 出于安全考虑,只把 VITE_ 开头的变量暴露给前端代码。这个改动有两面:

  • 把业务里用到的前端环境变量统一加 VITE_ 前缀。
  • 代码里 process.env.X 要改成 import.meta.env.VITE_X

但有些第三方库内部硬编码了 process.env.NODE_ENV,Vite 默认会 polyfill 这个,不用管。真正要小心的是自定义的、非 VITE_ 前缀的变量,迁移后会发现它们变成 undefined,而且往往要运行到那一行才报错。

坑三:生产构建的 base 路径

这个坑了我一晚上。项目部署在子路径 /admin/ 下,webpack 配 publicPath: '/admin/' 就行。Vite 对应的是 base 配置项:

// vite.config.ts
export default defineConfig({
  base: '/admin/',   // 注意末尾的斜杠,少了会出问题
})
记住 base 必须以斜杠开头和结尾。写成 '/admin'(没尾斜杠)会导致资源路径拼接错误,index.html 引用 JS 时变成 /adminassets/...,404。

迁移后的收益

  • 冷启动:40s → 1.8s。这个差距大到第一次跑的时候我以为命令错了。
  • HMR:3-4s → 几乎即时。改完保存,浏览器那边已经刷好了。
  • 构建体积:差不多持平,略减 5%(rollup 的 tree-shaking 比 webpack 稍好)。
  • 依赖数量:node_modules 从 1.2G 降到 800M 左右。

一点反思

迁移本身不难,难的是决定"现在要不要做"。老项目能用的时候,总觉得"再忍忍",但每次启动、每次热更新浪费的那几秒,乘以开发人数和天数,其实是很可观的成本。如果项目还要长期维护,早迁早享受。

另外,迁移这种事最好挑一个功能冻结的窗口期做,别一边迁一边加新功能,否则出问题都不知道是迁移引入的还是新功能的问题。