前言 :
很多团队不敢升级 Vite,主要顾虑有两点:
- 构建差异:怕生产环境打包出问题,导致线上故障。
- 生态不兼容:怕老旧的 CommonJS 依赖库报错。
我的建议是:不要直接删除 Webpack 配置! 采用"双轨制"策略------保留 Webpack 用于生产构建,先在开发环境引入 Vite 享受秒级启动。待 Vite 极其稳定后,再彻底替换。
接下来,我们将一步步拆解这个过程。
一、 准备工作:依赖分析
在动工之前,先看一眼 package.json。如果你的项目包含以下特征,迁移难度会升级:
- 使用了大量自定义的 Webpack Loader(需重写为 Vite 插件)。
- 强依赖
require.context(需改为import.meta.glob)。 - 使用了 Node.js Polyfill(如 Buffer, process 等,在浏览器端缺失)。
二、 第一步:环境与入口改造
1. 安装 Vite
bash
npm install vite @vitejs/plugin-vue (如果是Vue项目) --save-dev
# 或者
npm install vite @vitejs/plugin-react (如果是React项目) --save-dev
2. 移动 index.html
这是 Webpack 用户最不习惯的一步。
- Webpack :
public/index.html(作为模板) - Vite : 项目根目录
index.html(作为入口)
你需要把 index.html 移到根目录,并手动注入入口 JS:
html
<!-- 根目录 /index.html -->
<!DOCTYPE html>
<html lang="en">
<body>
<div id="app"></div>
<!-- 必须加上 type="module" -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
3. 创建基础配置
新建 vite.config.js。为了兼容老项目的 @ 别名,我们需要配置 alias。
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' // 或 react
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
// 兼容 Webpack 的别名习惯
'@': path.resolve(__dirname, 'src'),
'~': path.resolve(__dirname, 'node_modules') // 解决 scss 中 @import "~bootstrap"
},
// 如果你在导入文件时省略了后缀(不推荐,但老项目常见)
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}
})
三、 第二步:扫雷行动(常见报错与修复)
启动 npx vite,大概率你会看到满屏红字。别慌,我们一个一个杀。
坑位 1:require is not defined
Vite 基于 ESM,不支持 CommonJS 的 require。
-
场景 :
const data = require('./data.json') -
解法 :全局替换为 import。
javascriptimport data from './data.json' -
特殊情况 :如果是动态 require,请使用
import.meta.glob(详见第二篇博文)。
坑位 2:process is not defined
老项目中代码里充斥着 process.env.NODE_ENV 或自定义变量。
- 解法 :在
vite.config.js中使用define进行全局替换。
javascript
export default defineConfig({
define: {
// 简单粗暴地填充 process.env
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
BASE_URL: JSON.stringify('/')
},
// 如果有 global 变量依赖
'global': 'window'
}
})
坑位 3:JSX 在 .js 文件中报错
Webpack 的 babel-loader 可以配置在 .js 文件里写 JSX。但 Vite 使用 Esbuild,默认只支持在 .jsx 或 .tsx 中解析 JSX。
- 报错 :
The JSX syntax extension is not currently enabled - 解法 A (推荐) :批量重命名文件,把
.js改为.jsx。 - 解法 B (配置):强制开启 JSX 支持(不推荐,影响性能)。
javascript
// vite.config.js
export default defineConfig({
esbuild: {
loader: 'jsx', // 强制把所有文件当做 jsx 处理
include: /src\/.*\.js$/,
exclude: []
}
})
坑位 4:CSS 深度选择器报错
老项目常用 /deep/ 写法,这已经废弃且 Vite 不支持。
- 报错:Sass/Less 编译错误。
- 解法 :全局替换。
/deep/->::v-deep(Vue 2):deep(...)(Vue 3 标准写法)
四、 第三步:SVG 与图片处理差异
老项目可能使用 svg-sprite-loader 将 SVG 变成组件。Vite 中需引入 vite-plugin-svg-icons。
Webpack 写法:
javascript
const req = require.context('./icons', false, /\.svg$/)
requireAll(req)
Vite 迁移写法:
- 安装插件
npm i vite-plugin-svg-icons -D - 配置:
javascript
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
plugins: [
createSvgIconsPlugin({
// 指定图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
symbolId: 'icon-[dir]-[name]',
}),
]
- 入口引入:
import 'virtual:svg-icons-register'
五、 第四步:测试框架迁移 (Jest -> Vitest)
既然换了 Vite,原来的 Jest 可能会变得很慢(因为它还要走 babel 编译)。强烈推荐迁移到 Vitest。
Vitest 和 Vite 共用同一套配置,无需额外编译,速度极快。
- 安装:
npm i -D vitest - 配置
vite.config.js:
javascript
export default defineConfig({
test: {
globals: true, // 开启 describe, it, expect 全局变量
environment: 'jsdom',
},
})
- 大部分 Jest 用法(
expect,mock,spyOn)在 Vitest 中完全兼容,甚至 API 名字都一样。
六、 最终验证:双轨运行
在 package.json 中配置两个脚本:
json
"scripts": {
"serve": "vue-cli-service serve", // 老 Webpack 启动
"dev": "vite", // 新 Vite 启动
"build:webpack": "vue-cli-service build",
"build:vite": "vite build"
}
迁移策略总结:
- 开发阶段 :全员使用
npm run dev(Vite),享受秒级热更新,极大提升开发幸福感。 - 测试/生产阶段 :暂时保留
npm run build:webpack。 - 灰度切换 :当 Vite 在开发环境稳定运行 1-2 个月,且解决了所有构建时的 CSS/JS 兼容性问题后,尝试在测试环境使用
npm run build:vite。 - 完全替换:测试环境无误后,正式切换生产打包命令,删除 Webpack 相关依赖。
全系列总结
至此,我们的**《Vite 深入浅出》**五篇系列博文圆满结束。
我们从**"为什么要用 Vite"的认知篇开始,经历了 "基础配置"的手把手教学,深入了 "双引擎架构"的底层原理,探索了 "插件开发"的高级玩法,最后落实到了"老项目迁移"**的实战落地。
前端构建工具的发展史,就是一部**"用算法换时间"**的历史。从 Grunt/Gulp 的任务流,到 Webpack 的全量打包,再到 Vite 的按需编译,我们一直在追求更快的反馈循环。
Vite 不仅仅是一个工具,它代表了现代 Web 开发的标准:Native ESM,极速,且轻量。
希望这一系列文章能成为你前端工程化道路上的一块垫脚石。去重构吧,去创造吧,把生命浪费在美好的代码逻辑上,而不是进度条的等待中!