【导语】
老项目构建耗时从 120秒 → 18秒,热更新从 5秒 → 1秒以内。本文不聊Vite原理,只分享我们在迁移一个积累了3年的Vue2/React混合老项目时,遇到的真实致命坑点与最终解决方案。全程干货,建议收藏。
【目录】
-
为什么要迁移?数据说话
-
迁移前的风险预判(附兼容性自查表)
-
核心迁移步骤(环境变量、静态资源、Polyfill)
-
最坑的三个问题及源码级解决方案
-
最终构建产物对比与避坑建议
1. 为什么要迁移?
原Webpack配置历经多人之手,充斥着 raw-loader、file-loader 和大量 alias 嵌套。每次启动项目,CPU占满,早会前必去接杯咖啡。
迁移核心目标:开发服务器启动 < 3s,HMR < 1s,绝不修改业务源码逻辑。
2. 迁移前必做动作(血泪教训)
不要直接用 npm create vite 搬代码!先做兼容性检查:
| 检查项 | Webpack方案 | Vite方案 | 风险等级 |
|---|---|---|---|
| Node.js版本 | <= 14 | >= 16.20 | 高危 |
| CommonJS模块 | 直接require | 需转为ESM或vite-plugin-commonjs | 中危 |
| .env环境变量 | process.env | import.meta.env + 前缀限定 | 高危 |
⚠️ 关键决策:我们决定保留
process.env写法,利用 Vite 的define做替换,避免改业务代码。
3. 核心配置文件(可直接复用)
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig(({ mode }) => ({
plugins: [vue(), react()],
// 关键:兼容旧式 process.env
define: {
'process.env': {
NODE_ENV: JSON.stringify(mode),
// 将你的 .env.xxx 变量全部挂载进来
VUE_APP_API: JSON.stringify(process.env.VUE_APP_API)
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
// 兼容老项目中奇奇怪怪的缩写
'~': path.resolve(__dirname, './src')
}
},
server: {
port: 8080,
// 关键:让老项目中的 iframe 和第三方 cookie 正常
hmr: {
protocol: 'ws',
host: 'localhost'
}
},
build: {
// 关键:老项目 chunk 过多,防止内存溢出
chunkSizeWarningLimit: 2000,
rollupOptions: {
output: {
// 保持与原来 Webpack 一致的 chunk 命名规则,防止线上 CDN 404
entryFileNames: `assets/[name].[hash].js`,
chunkFileNames: `assets/[name].[hash].js`,
assetFileNames: `assets/[name].[hash].[ext]`
}
}
}
}));
4. 遇到的三个“要我命”的坑
坑位一:.scss 中的 ~@/assets 报错
-
现象:
Can't resolve '~@/assets/icon.png' -
原因:Webpack 的
~是解析前缀,Vite 不认。 -
解决:在
vite.config.js的resolve.alias里补一条'~@': path.resolve(__dirname, 'src'),并给css.preprocessorOptions添加额外配置:
css: {
preprocessorOptions: {
scss: {
additionalData: `$baseUrl: '/';`, // 干掉 ~ 写法
importer: (url) => {
if (url.startsWith('~@')) {
return { file: path.resolve(__dirname, 'src', url.slice(2)) };
}
return { file: url };
}
}
}
}
坑位二:全局注入的 jQuery 不见了
-
现象:
$ is not defined(老项目依赖 jQuery 插件)。 -
解决:千万别用
ProvidePlugin,Vite 推荐expose。直接用 esbuild 的 inject:
// 新建 jquery-shim.js
import $ from 'jquery';
window.jQuery = $;
window.$ = $;
// vite.config.js
export default defineConfig({
optimizeDeps: {
include: ['jquery'],
esbuildOptions: {
inject: [path.resolve(__dirname, 'jquery-shim.js')]
}
}
});
坑位三:第三方 UMD 库报错 exports is not defined
-
现象:某些老旧的富文本编辑器报错。
-
解决:在
index.html中直接用<script>标签加载,并在vite.config.js中开启build.commonjsOptions.transformMixedEsModules,同时把该库踢出优化:
optimizeDeps: {
exclude: ['old-editor']
}
5. 性能与构建产物对比
| 维度 | Webpack (4.x) | Vite (5.x) | 提升 |
|---|---|---|---|
| 冷启动 | 128s | 17s | ↓ 86% |
| 热更新 | 3~5s | < 500ms | ↓ 90% |
| 构建体积 (Gzip) | 8.2MB | 7.8MB | ↓ 5% (因Tree-shaking更优) |
最终建议:迁移不可怕,可怕的是盲迁。建议先封存分支,构建产物对线上绝对无影响后再合并。如果你也是维护型老项目,照着这份配置走,至少能省3天踩坑时间。