Chapter 10

从 webpack / CRA 迁移

手把手把一个既有 webpack / Create React App 项目迁到 Vite——配置对照、常见坑、验证清单一次给全。

10.1 迁移前的决策

10.2 高层配置对照

webpack 概念Vite 等价
entryindex.html 里的 <script type="module">
output.path / publicPathbuild.outDir / base
resolve.aliasresolve.alias
resolve.extensionsresolve.extensions
module.rules(loaders)插件 transform 钩子 / 内置支持
pluginsplugins(Rollup 插件接口)
devServer.proxyserver.proxy(相同语法)
DefinePlugindefine 配置项
HtmlWebpackPlugin内置:index.html 本身是入口
ProvidePluginoptimizeDeps.esbuildOptions.define 或 Rollup 插件
optimization.splitChunksbuild.rollupOptions.output.manualChunks

10.3 七步迁移清单

① 安装 Vite

$ npm i -D vite @vitejs/plugin-react-swc

② 把 public/index.html 挪到根目录

CRA 的 public/index.html 里:

<!-- 删除 CRA 特有占位符 -->
<!-- <div id="root"></div> --><div id="root"></div>

<!-- 在 body 末尾加入入口脚本(绝对路径) -->
<script type="module" src="/src/index.tsx"></script>

<!-- 删除 %PUBLIC_URL% 占位符,直接用绝对路径 -->
<link rel="icon" href="/favicon.ico">

③ 写最小 vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';

export default defineConfig({
  plugins: [react()],
  server: { port: 3000 },    // 贴近 CRA 习惯
});

④ 替换环境变量:REACT_APP_ → VITE_

// 旧
process.env.REACT_APP_API_URL
// 新
import.meta.env.VITE_API_URL

.env 里的变量名也改:REACT_APP_API_URLVITE_API_URL

⑤ 修复资源 import

// CRA 允许
import logo from './logo.svg';
// Vite 也允许,无需改

// CRA 里的 SVG as React component
import { ReactComponent as Logo } from './logo.svg';
// Vite 改为(需 vite-plugin-svgr)
import Logo from './logo.svg?react';

⑥ 更新 package.json 脚本

{
  "scripts": {
    "dev":     "vite",
    "build":   "tsc -b && vite build",
    "preview": "vite preview"
  }
}

⑦ 卸载 CRA / webpack 相关

$ npm uninstall react-scripts
# 或 webpack、webpack-cli、webpack-dev-server、babel-loader 等

10.4 常见坑

坑 1:process.env 在浏览器代码里用

webpack 靠 DefinePlugin 自动注入 process.env.NODE_ENV——Vite 不会。替代:

defineConfig({
  define: {
    'process.env': {},    // 避免 ReferenceError
  },
});

坑 2:Node 内置模块 polyfill

webpack 5 之前会自动 polyfill Bufferstreamcrypto——Vite 不做。遇到某包需要:

$ npm i -D vite-plugin-node-polyfills
import { nodePolyfills } from 'vite-plugin-node-polyfills';
defineConfig({
  plugins: [nodePolyfills()],
});

但更好的做法是换成浏览器原生方案——用 Uint8ArrayBuffercrypto.subtlenode:crypto

坑 3:CommonJS 包报错

某些老包只有 CommonJS 格式,Vite 默认走 ESM 会报错。把它加入预构建:

optimizeDeps: {
  include: ['lodash', 'legacy-cjs-pkg'],
}

esbuild 会把 CJS 转成 ESM。

坑 4:动态 require

// 不支持
const mod = require(`./locales/${lang}`);

// 改成 Vite glob
const locales = import.meta.glob('./locales/*.json');
const mod = await locales[`./locales/${lang}.json`]();

坑 5:CSS @import 路径解析

webpack 下 @import '~antd/dist/antd.css'~ 前缀代表 node_modules;Vite 不用 ~

@import 'antd/dist/antd.css';   /* 不要 ~ */

10.5 验证清单

10.6 性能基线对比

迁移完不妨测一下收益(同一台机、同一个项目):

# webpack
time npm run start   # 冷启动

# vite
time npm run dev     # 冷启动

# 修改一个组件的响应时间:浏览器 Performance 面板或手表

典型项目迁移后:冷启动 30s → 300ms,HMR 2s → 50ms,构建时间 60s → 10s。

10.7 Rolldown 展望

Rolldown 是 Void Zero / Evan You 团队用 Rust 重写的 Rollup。2025-2026 年逐步 GA,Vite 6.x 起可选启用,Vite 7 将默认使用。效果:

想提前体验:

$ npm i -D rolldown-vite
# 在 vite.config.ts 里替换 import 'vite' 为 import 'rolldown-vite'

10.8 全书总结

至此 10 章内容完成。Vite 正在重塑前端工具链,越早上越能享受开发速度红利。祝你 dev 服务器秒起、HMR 零延迟、build 不再等咖啡变冷。