我们把项目从Webpack迁移到了Vite 2.0,过程中踩了很多坑,也总结了很多实战经验。本文详细介绍了Vite 2.0的迁移过程、遇到的各种问题和解决方案、性能优化技巧、以及最佳实践。如果你在用Vite或者打算从Webpack迁移到Vite,希望这篇文章能帮你少走弯路。
一、为什么迁移到Vite
先说说我们为什么要从Webpack迁移到Vite。
我们的项目是一个中后台管理系统,用Vue 3 + TypeScript开发,最开始用的是Webpack。随着项目越来越大,Webpack的问题越来越明显:
- 启动慢:项目启动要30秒以上,改个配置重启要等很久
- 热更新慢:改一行代码,热更新要好几秒才能看到效果
- 构建慢:生产构建要好几分钟,CI/CD流水线很慢
- 配置复杂:Webpack的配置很复杂,loader和plugin一大堆,出了问题很难排查
后来Vite 2.0发布了,我们关注了一下,发现它的优势很明显:
- 启动快:基于浏览器原生ES模块,启动几乎是瞬间的,不管项目多大
- 热更新快:热更新是毫秒级的,改完代码立刻就能看到效果
- 构建快:生产环境用Rollup构建,比Webpack快很多
- 配置简单:Vite的配置很简洁,大部分功能开箱即用,不需要复杂的配置
我们决定先在一个新项目中试用Vite,效果很好。然后就开始把老项目从Webpack迁移到Vite。迁移的过程中踩了很多坑,但是最终都解决了。迁移完成之后,开发体验提升了很多。
本文就来分享我们的迁移过程、踩坑经验和最佳实践。
二、迁移过程
我们的迁移过程大概分为几个步骤。
第一步:了解Vite的概念
迁移之前,先了解Vite和Webpack的区别。Vite在开发环境用的是浏览器原生的ES模块,不需要打包,所以启动快。生产环境用Rollup打包,和Webpack类似。
Vite的配置文件是vite.config.js(或者.ts),和Webpack的配置方式不一样。Vite有自己的插件系统,和Webpack的loader/plugin不兼容。
了解了这些基本概念之后,就可以开始迁移了。
第二步:创建Vite配置
在项目根目录创建vite.config.js,配置入口、别名、插件等。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
})Vite的配置比Webpack简洁很多,大部分功能开箱即用。
第三步:修改index.html
Vite的index.html和Webpack的不一样。Vite的index.html在项目根目录,而且需要在里面引入入口JS文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>注意script标签要加type="module",而且路径是绝对路径,从项目根目录开始。
第四步:替换Webpack特有的语法
Webpack中有一些特有的语法,在Vite中不支持,需要替换:
require.context:Vite中用import.meta.glob代替process.env:Vite中用import.meta.env代替- 静态资源引入:Vite中用new URL或者直接import
- CSS中的~别名:Vite中不需要~,直接用别名
这些替换是迁移过程中最繁琐的部分,需要逐个文件修改。
第五步:安装依赖和测试
修改完代码之后,安装Vite相关的依赖,然后启动开发服务器测试。
npm install vite @vitejs/plugin-vue
npx vite启动之后,测试各个功能是否正常,发现问题就修复。我们在这个阶段遇到了很多问题,下面会详细说。
第六步:生产构建测试
开发环境没问题之后,测试生产构建。
npx vite build
npx vite preview构建完成之后,用vite preview预览构建产物,测试功能是否正常。生产构建也可能遇到一些问题,需要修复。
整个迁移过程,我们花了大概一周的时间。大部分时间都花在解决各种兼容性问题上。
三、踩坑总结
迁移过程中,我们踩了很多坑。下面是一些主要的坑和解决方案。
坑1:require.context不支持
Webpack中的require.context可以批量引入文件,Vite中不支持。Vite中用import.meta.glob来代替。
// Webpack
const modules = require.context('./modules', false, /\.js$/)
modules.keys().forEach(key => {
const module = modules(key)
// ...
})
// Vite
const modules = import.meta.glob('./modules/*.js')
for (const path in modules) {
const module = await modules[path]()
// ...
}注意import.meta.glob返回的是异步的,需要用await或者then。如果需要同步引入,可以用import.meta.globEager(Vite 3之后改成了{ eager: true }选项)。
坑2:process.env不支持
Webpack中用process.env来访问环境变量,Vite中用import.meta.env。
// Webpack
const apiUrl = process.env.VUE_APP_API_URL
// Vite
const apiUrl = import.meta.env.VITE_API_URL注意Vite中环境变量的前缀是VITE,不是VUEAPP。只有以VITE开头的变量才会暴露到客户端。
如果代码中用了很多process.env,可以用define配置做一个全局替换:
define: {
'process.env': {}
}但是不建议这样做,最好还是逐个替换成import.meta.env。
坑3:CommonJS模块的问题
Vite在开发环境用的是ES模块,但是有些第三方库还是CommonJS格式的,直接引入会报错。
Vite会自动预构建CommonJS的依赖,转换成ES模块。但是有些库比较特殊,预构建可能会出问题。
解决方案:
- 把有问题的库加入optimizeDeps.include,强制预构建
- 如果库有ES模块版本,优先用ES模块版本
- 实在不行,可以用vite-plugin-commonjs插件来处理
optimizeDeps: {
include: ['some-commonjs-lib']
}坑4:静态资源引入的问题
Webpack中引入静态资源的方式,在Vite中有些不一样。
// Webpack
const img = require('@/assets/logo.png')
// Vite
import img from '@/assets/logo.png'
// 或者
const img = new URL('@/assets/logo.png', import.meta.url).hrefCSS中引入图片,Vite和Webpack差不多,但是要注意路径的问题。Vite中CSS的url()路径是相对于CSS文件的,和Webpack一致。
如果是动态引入图片,需要用new URL的方式:
const imgUrl = new URL(`./assets/${name}.png`, import.meta.url).href坑5:别名配置的问题
Vite的别名配置和Webpack有点不一样。Webpack中resolve.alias的键可以不带$,Vite中建议带精确匹配。
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'vue': 'vue/dist/vue.esm-bundler.js'
}
}如果别名匹配有问题,可以用对象数组的形式,更精确:
resolve: {
alias: [
{ find: '@', replacement: path.resolve(__dirname, 'src') }
]
}坑6:CSS预处理器的问题
Vite支持CSS预处理器(Less、Sass、Stylus),但是需要单独安装对应的依赖。
npm install less -D
npm install sass -DVite中CSS变量的注入和Webpack不一样。Webpack中用style-resources-loader,Vite中用css.preprocessorOptions:
css: {
preprocessorOptions: {
less: {
additionalData: `@import "@/styles/variables.less";`
}
}
}坑7:代理配置的问题
Vite的代理配置和Webpack devServer的代理类似,但是有些细节不一样。
Vite的代理用的是http-proxy,配置方式和Webpack差不多:
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}注意Vite的代理rewrite函数和Webpack的pathRewrite写法不一样。
如果代理WebSocket,需要加ws: true:
'/ws': {
target: 'ws://localhost:8080',
ws: true
}坑8:热更新不生效
有时候改了代码,但是热更新不生效,需要手动刷新。常见的原因:
- 组件的name没有设置,或者设置的不对
- 用了高阶组件,导致热更新识别不了
- 文件的大小写和导入不一致(Windows不区分大小写,但是Vite区分)
- 循环依赖导致热更新异常
解决方案:
- 确保每个组件都有正确的name
- 检查文件路径的大小写
- 用vite --force清除缓存重新启动
- 避免循环依赖
坑9:构建产物的问题
生产构建的时候,可能会遇到一些问题:
- 构建产物太大:需要做代码分割和懒加载
- 某些库构建报错:可能是CommonJS的问题,需要配置build.commonjsOptions
- 静态资源路径不对:需要配置base路径
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
ui: ['element-plus']
}
}
},
commonjsOptions: {
transformMixedEsModules: true
}
}坑10:TypeScript的问题
如果用TypeScript,需要注意:
- Vite不做类型检查,类型错误不会阻止构建。需要单独运行tsc --noEmit做类型检查
- import.meta.env的类型需要在vite-env.d.ts中声明
- 路径别名需要在tsconfig.json中配置paths
// vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}四、性能优化
迁移到Vite之后,我们还做了一些性能优化。
1. 依赖预构建优化
Vite会自动预构建依赖,但是可以通过配置来优化:
optimizeDeps: {
include: ['vue', 'vue-router', 'pinia', 'element-plus'],
exclude: ['some-esm-only-lib']
}把常用的依赖加入include,确保预构建,避免运行时再转换。
2. 代码分割
生产构建的时候,用manualChunks把大的第三方库单独打包,利用浏览器缓存:
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
ui: ['element-plus'],
utils: ['lodash-es', 'dayjs']
}
}
}
}3. 路由懒加载
路由组件用动态导入,实现懒加载,减小首屏体积:
const Home = () => import('@/views/Home.vue')
const About = () => import('@/views/About.vue')4. 图片优化
大图片用webp格式,小图片用base64内联。Vite默认会把小于4KB的图片转成base64,可以通过assetsInlineLimit调整:
build: {
assetsInlineLimit: 4096
}也可以用vite-plugin-imagemin插件来压缩图片。
5. Gzip压缩
构建的时候生成gzip压缩文件,服务器开启gzip,减小传输体积:
import viteCompression from 'vite-plugin-compression'
plugins: [
viteCompression({
algorithm: 'gzip'
})
]五、最佳实践
总结一些Vite的最佳实践。
1. 用ES模块
尽量用ES模块的语法,避免CommonJS。ES模块是Vite的原生格式,性能最好,也最不容易出问题。
2. 环境变量用VITE_前缀
客户端的环境变量一定要用VITE前缀,否则不会暴露到客户端。敏感的变量不要加VITE前缀,只在服务端使用。
3. 路径别名统一配置
路径别名在vite.config.js和tsconfig.json中都要配置,保持一致。用@作为src的别名是最常见的做法。
4. 组件命名规范
组件文件用PascalCase命名,组件内部设置name属性,方便调试和热更新。
5. 定期更新Vite版本
Vite更新很快,新版本会修复很多bug,提升性能。建议定期更新到最新的稳定版本,但是更新之前要在测试环境充分验证。
6. 保留Webpack配置
迁移的时候,不要立刻删除Webpack的配置。可以先保留,等Vite稳定运行一段时间之后再删除。万一Vite出了问题,可以快速回滚到Webpack。
六、迁移效果
迁移完成之后,效果很明显:
- 启动时间:从30秒降到了1秒以内
- 热更新时间:从3-5秒降到了100毫秒以内
- 构建时间:从3分钟降到了1分钟
- 配置复杂度:从几百行的Webpack配置降到了几十行的Vite配置
- 开发体验:大大提升,改完代码立刻看到效果,开发效率提升了很多
团队的开发人员都表示,用了Vite之后再也回不去Webpack了。
七、写在最后
Vite 2.0是一个很优秀的构建工具,它的开发体验比Webpack好很多。虽然迁移的过程中踩了一些坑,但是最终的效果很值得。
如果你还在用Webpack,并且被慢启动和慢热更新困扰,我建议你尝试一下Vite。它不会让你失望的。
迁移的时候,不要着急,一步一步来。先了解Vite的概念,然后创建配置,修改代码,测试功能,解决问题。遇到问题不要慌,大部分问题都有解决方案,网上也有很多资料可以参考。
Vite还在快速发展,未来会越来越好。作为前端开发者,我们要保持学习,跟上工具的发展,用更好的工具提升开发效率。
最后用一句话结束本文:"工欲善其事,必先利其器。"愿每一个前端开发者都能用上Vite这样的好工具,让开发变得更高效、更愉快。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录