前言
Vite 已经取代 Webpack 成为新项目的默认选择,但存量 Webpack 项目怎么迁?Eject 的 CRA 项目怎么处理?本文从原理到实操,覆盖完整迁移路径。
一、原理差异:为什么 Vite 这么快
Webpack 冷启动流程
1. 读取所有入口文件和依赖
2. 通过 Loader 转换每一个模块
3. 构建完整的模块依赖图
4. 将模块打包成 Bundle(一个或多个 chunk)
5. 启动 Dev Server 提供服务
—— 项目越大,步骤 2-3 越慢
Vite 冷启动流程
1. esbuild 预构建 node_modules 依赖(仅首次)
2. 启动 Dev Server
3. 浏览器请求时,按需编译对应模块为 ESM
—— 启动只做步骤 1-2,O(1) 复杂度
核心差异:Vite 利用浏览器原生 ESM 能力,不做全量打包,按需编译。
二、迁移对照表
| 配置项 | Webpack | Vite |
|---|---|---|
| 入口 | entry: './src/index.js' | index.html 中 script src |
| 别名 | resolve.alias | resolve.alias(几乎相同) |
| 环境变量 | DefinePlugin | define |
| CSS 预处理 | sass-loader | 内置,安装 sass 即可 |
| 静态资源 | file-loader | 内置,直接 import |
| 代理 | devServer.proxy | server.proxy |
| 代码分割 | splitChunks | build.rollupOptions |
三、完整迁移配置
// vite.config.ts — 迁移对照
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
// == Webpack entry → Vite 用 index.html ==
// == resolve.alias →
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
},
// == DefinePlugin →
define: {
'process.env.APP_VERSION': JSON.stringify('1.0.0'),
__DEV__: JSON.stringify(true),
},
// == sass-loader options →
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables" as *;`,
},
},
},
// == devServer.proxy →
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// == splitChunks →
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
antd: ['antd'],
},
},
},
// Webpack 默认 244KB → Vite 也建议配置
chunkSizeWarningLimit: 500,
},
plugins: [react()],
});
四、常见迁移踩坑
4.1 require.context → import.meta.glob
// Webpack
const modules = require.context('./locales', false, /\.json$/);
modules.keys().forEach(key => modules(key));
// Vite
const modules = import.meta.glob('./locales/*.json');
Object.entries(modules).forEach(([path, loader]) => {
loader().then(mod => console.log(path, mod.default));
});
// 同步加载模式
const modules = import.meta.glob('./locales/*.json', { eager: true });
4.2 process.env → import.meta.env
// ❌ Webpack 特有,Vite 中不可用
const apiUrl = process.env.REACT_APP_API_URL;
// ✅ Vite 方式
const apiUrl = import.meta.env.VITE_API_URL;
// 兼容方案:在 vite.config.ts 中 define
define: {
'process.env.REACT_APP_API_URL': JSON.stringify(process.env.REACT_APP_API_URL),
}
4.3 CommonJS 依赖兼容
// vite.config.ts
export default defineConfig({
optimizeDeps: {
include: ['cjs-dependency-name'], // 强制预构建 CJS 模块
},
build: {
commonjsOptions: {
include: [/node_modules/], // CJS → ESM 转换范围
},
},
});
五、迁移检查清单
| 步骤 | 操作 | 风险等级 |
|---|---|---|
| 1 | 安装 Vite + 对应框架插件 | 🟢 低 |
| 2 | 移动 index.html 到根目录 | 🟢 低 |
| 3 | 创建 vite.config 对照迁移 | 🟡 中 |
| 4 | 替换 process.env → import.meta.env | 🟡 中 |
| 5 | 替换 require.context → import.meta.glob | 🟡 中 |
| 6 | 处理 CJS 依赖兼容 | 🔴 高 |
| 7 | 调整环境变量注入方式 | 🟡 中 |
| 8 | 测试构建产物是否一致 | 🔴 高 |
迁移建议:小项目(<50 个源文件)直接一把梭。大中型项目建议渐进式迁移——先用 Module Federation 或微前端方式让 Webpack 和 Vite 并存,逐步切流量验证。