从Webpack到Vite:覆盖300+页面的老项目迁移实战总结

2 阅读3分钟

【导语】
老项目构建耗时从 120秒 → 18秒,热更新从 5秒 → 1秒以内。本文不聊Vite原理,只分享我们在迁移一个积累了3年的Vue2/React混合老项目时,遇到的真实致命坑点最终解决方案。全程干货,建议收藏。

【目录】

  1. 为什么要迁移?数据说话

  2. 迁移前的风险预判(附兼容性自查表)

  3. 核心迁移步骤(环境变量、静态资源、Polyfill)

  4. 最坑的三个问题及源码级解决方案

  5. 最终构建产物对比与避坑建议


1. 为什么要迁移?

原Webpack配置历经多人之手,充斥着 raw-loaderfile-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.envimport.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)提升
冷启动128s17s↓ 86%
热更新3~5s< 500ms↓ 90%
构建体积 (Gzip)8.2MB7.8MB↓ 5% (因Tree-shaking更优)

最终建议:迁移不可怕,可怕的是盲迁。建议先封存分支,构建产物对线上绝对无影响后再合并。如果你也是维护型老项目,照着这份配置走,至少能省3天踩坑时间。