Develop

前端工程化演进:从 Webpack 到 Vite 的迁移实战与原理剖析

✎ -- 字 🕐 -- 分钟
字号
Webpack vs Vite 架构对比Webpack (Bundle-based)源码 → Loader → 依赖图 → Chunk → Bundle全量打包后启动 Dev Server大型项目冷启动 30s-2minHMR: 模块级替换,快Vite (ESM-based)源码 → esbuild(预构建) → 按需编译 → ESM启动即服务,浏览器按需请求冷启动 < 1s(无论项目大小)HMR: 几乎即时(ESM 原生)

前言

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 能力,不做全量打包,按需编译。

二、迁移对照表

配置项WebpackVite
入口entry: './src/index.js'index.html 中 script src
别名resolve.aliasresolve.alias(几乎相同)
环境变量DefinePlugindefine
CSS 预处理sass-loader内置,安装 sass 即可
静态资源file-loader内置,直接 import
代理devServer.proxyserver.proxy
代码分割splitChunksbuild.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 并存,逐步切流量验证。