给一个 Vue2 老项目做 Webpack 到 Vite 迁移

接手过老项目的人都懂那种感受:改一行代码,等热更新等到手停在键盘上发呆。我负责迁移的这个项目是一个跑了多年的广告投放前端工程,Vue2 + Webpack,模块一年年长出来,启动和热更新的等待时间也一年年长出来。

迁移到 Vite 不是为了追新。真实动机就两个:开发时的等待成本,和 Webpack 配置本身的维护成本——那份配置文件经手过很多人,loader 和 plugin 叠了一层又一层,没人敢动。

这篇记录迁移过程中真正花时间的几个地方。不是教程,更像一份”如果重来一遍,我会先看哪里”的清单。

Vue2 项目跑 Vite,第一件事是插件层

Vite 官方模板只认 Vue3。Vue2 项目要跑起来,得先解决 SFC 编译:

  • Vue 2.7+:用官方的 @vitejs/plugin-vue2
  • Vue 2.6 及以下:用社区的 vite-plugin-vue2

这一步看起来只是装个插件,但有个隐蔽的前提:template 编译器版本必须和运行时版本严格一致。Vue2 的 vue-template-compilervue 是成对的,版本对不上启动时会直接报错——报错信息倒是清楚,真正麻烦的是有些老项目锁版本的方式很随意,package.json 里写的和 lock 文件里装的不是一回事,先对齐版本再动手。

另外两个连带问题:

  • 项目里如果有 JSX 写法(Vue2 时代不少高阶组件这么写),要额外配 Vue2 的 JSX 插件,不配的话报错位置会出现在很奇怪的地方。
  • Element UI 的按需加载原来靠 babel-plugin-component,Vite 下这条路不通。我的做法是先全量引入把项目跑通,再换 unplugin-vue-components 的 resolver 做按需——迁移期一次只解决一类问题,不要把”能跑”和”跑得好”混在一个阶段。

环境变量:最容易”看起来没问题”的部分

Webpack 时代的习惯是 process.env.VUE_APP_XXX,Vite 的口径是 import.meta.env.VITE_XXX。改配置文件只要十分钟,真正的工作量在业务代码里。

我踩的最深的一个坑:老代码里埋点开关是用 process.env.VUE_APP_ENV 判断的,散落在多个业务文件里。迁移后构建能通过,页面也能打开——但 process 在浏览器里根本不存在,某些分支一执行就是 process is not defined。而且这种错误只在特定操作路径下触发,冒烟测试根本测不出来。

处理方式分两步:

  1. 过渡期先用 defineprocess.env.VUE_APP_* 整体映射掉,保证任何遗漏的读取点都不会炸;
  2. 再把散落的读取点逐步收敛到一个统一的 env 模块,业务代码只从这个模块拿配置。

这个坑给我留下的习惯是:迁移前先全局搜一遍 process.env,把所有出现位置列成清单。清单比记忆可靠。

require.context 和动态 require

Webpack 对模块加载很宽容:require()、运行时拼路径、require.context 批量导入,都能跑。Vite 基于 ESM,要求导入路径静态可分析——这是两种构建哲学的根本差异,也是迁移中真正要”改代码”而不是”改配置”的部分。

分三类处理:

  • require.context 批量导入(路由、store 模块、全局组件注册最常见):换成 import.meta.glob。注意它是编译期展开的,路径规则要写死,不能运行时拼。
  • 普通 require():能改的直接改成静态 import 或动态 import()
  • 运行时拼路径的 require(比如按接口返回的名字加载图片):这类没有银弹,要么用 import.meta.glob 预先建立映射表,要么把资源挪到 public 目录走 URL 引用。哪种合适取决于资源数量和更新频率。

第三方 CJS 依赖大部分靠 Vite 的依赖预构建就能吃掉,个别顽固的再用 alias 指到 ESM 版本或单独配置。我的经验是:不要一上来就给每个报错找插件,先分清是”项目代码要改”还是”依赖要兼容”,前者占的比例比想象中大。

静态资源和别名

这部分琐碎但不难,列几个必查项:

  • resolve.alias:把 Webpack 的别名逐条搬过来,注意 CSS 里 ~@/assets 这种带 ~ 前缀的写法 Vite 不认,要全局清理。
  • base:对应 Webpack 的 publicPath,部署在子路径或走 CDN 的项目,构建后资源 404 十有八九是它。
  • 写死的 /static 路径:老项目常见,统一梳理成 import 引用或挪进 public

最不能省的一步:回归

构建工具迁移最怕的不是报错,是”本地能跑、线上白屏”。开发环境 Vite 用 esbuild 做依赖预构建,生产构建走 Rollup——两条链路,某些依赖开发能跑、build 就挂是真实会发生的,所以 vite build 加产物预览必须纳入验证,不能看 dev server 正常就收工。

我当时的回归清单按业务主链路列:登录、菜单权限、核心业务页面、接口代理、静态资源、埋点上报、异常监控。埋点和监控最容易被漏——它们不影响页面显示,坏了没人第一时间发现,但对业务是暗伤。

迁移完,值不值?

说实话,我当时没有保留严格的启动/HMR 耗时对比数据,所以给不出”提升了 X%”这种数字——这也是复盘时最大的遗憾:下次做这类迁移,第一步应该是记基准数据,启动耗时、HMR 耗时、build 耗时、产物体积,前后各测一轮,收益才有据可查。

能确定的是体感:本地启动从”起身倒水”变成”眨眼”,热更新基本无感,配置从一份没人敢碰的遗产变成一份看得懂的声明。对一个还要继续维护多年的老项目,这笔账是划算的。

最后一点心得:老项目迁移的难度不在 Vite 本身,在于你得先弄清楚这个项目这些年积累了哪些”隐性约定”——哪些环境变量在哪些地方被读、哪些资源路径是写死的、哪些依赖是 CJS。迁移工具链的过程,本质上是把这些隐性约定一条条显性化的过程。工具换完,项目也顺便被重新理解了一遍。

给一个 Vue2 老项目做 Webpack 到 Vite 迁移

http://example.com/2025/11/04/vue2-webpack-to-vite/

作者

Jun

发布于

2025-11-04

更新于

2026-07-07

许可协议