从 Webpack 到 Vite:30 个存量项目的迁移踩坑与灰度方案

去年年底我们做了一件拖了两年的事:把团队 30 个中后台项目从 webpack 4 迁到 Vite 5。不是新项目试点,是存量项目全量换底座。三个月里我踩的坑,大概够写一篇长文,这里挑最值钱的部分记下来。

先说结论:收益是真的,但收益不在「构建更快」,而在「改一行代码到看到结果只要 200ms」这件事改变了写代码的手感。 至于迁移成本,跟项目的「野」程度强相关,跟你项目大小关系不大。

一、先量一量,别凭感觉迁

动手前我写了个脚本,把 30 个项目按四个维度打了分:

维度 判断方式 风险
依赖体积 node_modules 里 CJS 包占比 高
动态引用 require( 变量 ) 出现次数 高
自定义 loader 自研 loader / 特殊 webpack 插件 高
环境判定 process.env.* 出现次数 中

最后只有 9 个项目是「低风险」,可以直接换。剩下 21 个,得先做改造再迁移。这一步花了两周,但它决定了后面不会出现「迁到一半发现某个包根本跑不起来」的尴尬。

二、冷启动从 42s 到 3.4s,代价是首屏多了一次请求

webpack 时代,最大的项目冷启动 42s,改一个文件 HMR 1.8s。Vite 下分别是 3.4s 和 200ms 左右。

但 Vite 的「快」是把编译推迟到了浏览器请求时。开发环境里,首屏会看到一大堆 module 请求瀑布。项目里有 200+ 路由懒加载时,这个瀑布会很明显。我们的做法是:

ts 复制代码
// vite.config.ts
export default defineConfig({
  server: {
    warmup: {
      // 预热首页和登录后必到的几个页面
      clientFiles: ['./src/pages/home/index.tsx', './src/pages/login/index.tsx'],
    },
  },
})

配合 optimizeDeps.include 把高频依赖预构建,首屏瀑布能压掉一半左右。

三、CJS 依赖:最烦的不是报错,是报错很晚

webpack 对 CJS 的兼容是「顺手就做了」,Rollup 不是。表现是:构建能过,运行时报 Cannot read properties of undefined。

三个必须提前处理的点:

  1. Node 内置模块 。老代码里 import path from 'path'、process.cwd() 到处都是,浏览器里没有。要么 polyfill,要么在 define 里塞进去:
ts 复制代码
define: {
  'process.env': JSON.stringify({}),
  __dirname: JSON.stringify('/'),
}
  1. 动态 require 。require('./locales/' + lang) 这种,Rollup 静态分析不出来。改成 import.meta.glob:
ts 复制代码
const modules = import.meta.glob('./locales/*.json', { eager: true })
const dict = modules[`./locales/${lang}.json`].default
  1. default 互操作 。import _ from 'lodash' 在某些 CJS 包里拿到的是 module 对象而不是函数。@rollup/plugin-commonjs 的 requireReturnsDefault: 'auto' 能救一部分,剩下的只能一个个改。

四、真正难的是灰度,不是编译

30 个项目不可能一次性全切。我们的方案是双构建并行 + 入口分流:

  • webpack 构建产物仍然发布,Vite 产物发布到独立的静态目录;
  • 网关按 用户 ID hash % 100 决定这次请求返回哪份 index.html;
  • 灰度比例 1% → 5% → 20% → 50% → 100%,每档观察 24 小时的 JS 错误率和首屏 P75。

关键是回滚要能在 1 分钟内完成。因为只是改网关的分流规则,回滚就是改一个比例数字,比回滚代码发布快得多。这条比任何优化都重要------它让我们敢在白天灰度。

五、几个事后才明白的事

  • 别在迁移期间顺手升级依赖。两个变量一起动,出问题定位成本翻倍。
  • vite build 和 vite dev 是两套行为。dev 下能跑不代表 build 后能跑,CI 里必须跑一次完整构建 + 冒烟。
  • 产物体积不一定会变小 。Rollup 的 tree-shaking 更彻底,但 chunk 切分策略不同,某些项目产物反而大了 5%。需要重新调 manualChunks。
  • 团队心智成本被低估了 。public/ 与 src/assets 的引用规则、index.html 变成入口,这些小事每个人都会问一遍。我们写了一页「Vite 速查」贴在群里,省了大量重复答疑。

写在最后

迁完之后,最直观的变化不是构建时间,是「保存即刷新」带来的一种心理上的轻松:改样式不用等,改逻辑不用猜。这种手感上的收益,是很难用数字汇报、但每个人都能感知到的。

如果你们也在评估,我的建议是:先挑一个最野的项目做,把它做成模板;剩下的项目迁移就是复制粘贴加改配置。 反过来先拿最简单的项目试点,会得到一个「看起来很顺利」的假象。

相关推荐
晓得迷路了1 天前
栗子前端技术周刊第 153 期 - Vitest 5.1、Remix v3.2、Node.js 27 Alpha...
前端·javascript·vite
liuchangng10 天前
React 19 + Vite 6 实战:从教材 PDF 到 866 词间隔重复单词 App
前端·react.js·pdf·vite·开源项目
小阿鑫15 天前
尤雨溪 : Vue 4 已经在规划中了!
vue·vite·vite+·程序员海军·vue4
YFF菲菲兔18 天前
EvaluatedModules
vite
Java后端的Ai之路19 天前
前端技术Vue-13-安装webpack与vuejs
前端·vue.js·webpack
Java后端的Ai之路20 天前
前端技术Vue-09-webpack-vuejs-实战
前端·vue.js·webpack
定栓23 天前
Webpack迁到Vite第七节(二):全家桶移除 — Babel 全家桶深度分析
前端·webpack·node.js
定栓23 天前
Webpack迁到Vite第七节(一):全家桶移除 — 配置文件与构建核心
前端·webpack·node.js