vue2.7+webpack迁移vite实践总结
前言
众所周知,得益于大多数浏览器都支持了ES模块,Vite基于esbuild在开发环境有很不错的启动速度。随着我们项目文件越来越多,并且有多个工程经常需要切换、更换ip、更换用户token等,再加上webpack中每次更改这些内容都需要重新启动,而vite是热更新的,就很方便。为了提升开发效率的同时,不影响线上打包结果,决定将开发环境从webpack迁移至vite,打包依然使用webpack。
本文记录了迁移过程遇到的问题与解决办法。
迁移过程
npm i -D vite
,安装vite,根目录创建vite.config.js
文件。
@vitejs/plugin-vue2
项目是vue@2.7
版本,需要安装@vitejs/plugin-vue2,2.7之前的版本安装vite-plugin-vue2
// vite.config.js
import vue from '@vitejs/plugin-vue2'
export default {
plugins: [vue()]
}
为了兼容webpack,新建了一个index.vite.html
文件作为入口,添加<script type="module" src="./main.js"></script>
(后面配置了插件后删除了),在index.html
中的 URL 将被自动转换,移除使用到的 %PUBLIC_URL%
等占位符。
添加启动脚本
"scripts": {
...
"dev": "vite --host",
},
alias移植
webpack中使用到了alias别名,移植配置:
// vite.config.js
import vue from '@vitejs/plugin-vue2'
export default {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
...
}
}
plugins: [vue()]
}
vite-plugin-html
vite的index.html
默认在项目最外层需要重新指定,并且之前 webpack 的 HtmlWebpackPlugin
插件中配置了变量,需要借助vite-plugin-html
指定入口与声明变量:
// vite.config.js
import vue from '@vitejs/plugin-vue2'
import { createHtmlPlugin } from 'vite-plugin-html'
const orderCdn = {
js: [...],
css: [...],
}
export default {
...
plugins: [
vue(),
createHtmlPlugin({
minify: true,
inject: {
data: {
orderCdn: orderCdn
},
},
// 注:指定entry后,不需要在index.html添加script标签,若添加了建议删除
entry: 'src/projects/portal/main.js',
template: 'src/projects/portal/index.vite.html',
}),
]
}
...
替换htmlWebpackPlugin变量
// index.vite.html
...
- <% if(htmlWebpackPlugin.options.orderCdn) { %> <%for(var css of
- htmlWebpackPlugin.options.orderCdn.css){ %>
+ <% if(orderCdn) { %> <%for(var css of orderCdn.css){ %>
<link rel="stylesheet" type="text/css" href="<%= css %>" />
<% } %> <% } %>
resolve.extensions
使用webpack导入文件通常习惯省略后缀名,例如:import App from './App'
,vite启动会提示找不到文件,需要补全后缀名:import App from './App.vue'
; 补全数量较多,可以配置resolve.extensions
:导入时想要省略的扩展名列表,默认不包含.vue后缀。
注:官方不建议忽略自定义导入类型的扩展名(例如:.vue
),因为它会影响 IDE 和类型支持。
// vite.config.js
export default {
resolve: {
...
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
...
}
process.env
项目内使用了process.env.NODE_ENV
判断环境,报错process
未定义,配置define
:定义全局常量替换方式。
...
define: {
'process.env': process.env
}
vite-plugin-externals
webpack配置了externals,同步vite需要安装vite-plugin-externals
// vite.config.js
import { viteExternalsPlugin } from 'vite-plugin-externals'
export default {
...
plugins: [
...
viteExternalsPlugin({
'orderHeader': 'orderHeader',
}),
]
}
这里遇到了一个坑,配置完运行Vue.use(OrderHeader)
依然提示找不到组件:
插件转换前和转换后代码如下:
// source code
import OrderHeader from 'orderHeader'
// transformed
const OrderHeader = window['orderHeader']
输出打印一下OrderHeader,发现是有内容的,多包了一层default;
而webpack通过编译构建,会做兼容性处理。
这里可以通过打印输出结果看到OrderHeader
被替换成了 orderHeader__WEBPACK_IMPORTED_MODULE_0___default.a
,而orderHeader__WEBPACK_IMPORTED_MODULE_0___default
函数实际执行的是getDefault()
源码如下:

解决:判断process.env.NODE_ENV
在开发环境中使用Vue.use(OrderHeader.default)
@vitejs/plugin-vue2-jsx
由于有部分.vue
文件使用了JSX,需要单独安装插件@vitejs/plugin-vue2-jsx。(注:@vitejs/plugin-vue2不再处理特有的 JSX / TSX 转换,vite-plugin-vue2则可以直接配置)。配置完还是不行,排查后发现.vue
文件中需要在 script
标签,添加 lang="jsx"
, 例: <script lang="jsx"></script>
// vite.config.js
import vue from '@vitejs/plugin-vue2'
import vueJsx from '@vitejs/plugin-vue2-jsx'
export default {
...
plugins: [
vue(),
vueJsx({})
...
]
}
由于文件较多,使用了vite-plugin-lang-jsx
插件自动注入,不过实际使用的时候发现插件有bug
会导致vite
服务挂起白屏。查看了源码后发现,该插件内部实际实现了两个功能:
plugin1
:为js
文件添加lang=jsx
后缀;plugin2
:为.vue
文件添加lang="jsx"
;
最后导出return [plugin1, plugin2];
。经测试挂起是由于plugin1
导致的,正好我们项目只需要用到plugin2
,所以最后配置成:
// vite.config.js
import vue from '@vitejs/plugin-vue2'
import vueJsx from '@vitejs/plugin-vue2-jsx'
import langJsx from 'vite-plugin-lang-jsx'
export default {
...
plugins: [
// 必须写在vue、vueJsx之前
langJsx()[1],
vue(),
vueJsx({})
...
]
}
server.proxy
页面接口没有正常转发,查询配置发现devServer.proxy
需要设置成server.proxy
,并且proxy的pathRewrite
调整成rewrite
:
server:{
proxy:{
'/api':{
rewrite: (path) => path.replace(/^/\api/, ''),
}
}
{
vite-plugin-static-copy
main.js
入口处读取的配置文件,提示404 Not Found。
对比源代码与输出结果,vite将相对路径输出成绝对路径,但是没有对入口处读取配置文件代码内的地址做转换。

找到两个办法自动输出完整路径:
- import导入添加
?url
后缀显式导入为一个 URL; - 使用import.meta.url结合原生的 URL 构造器 获取路径。
但是测试了下都不兼容webpack。
import rootConfig from './configs/root.config.json?url'
const jsonUrl = new URL('./configs/root.config.json', import.meta.url).href
// 输出
// rootConfig:
/src/projects/portal/configs/root.config.json
// jsonUrl
http://localhost:5173/src/projects/portal/configs/root.config.json
root文件实际也引用了多个文件。针对这样的静态资源,最后使用了vite-plugin-static-copy
处理,支持开发环境处理静态资源。(一开始配错地址,以为不支持开发环境,就跑偏了😅)
import { viteStaticCopy } from 'vite-plugin-static-copy'
export default {
...
plugins: [
...
viteStaticCopy({
targets: [
{
src: `src/projects/portal/configs/**`,
dest: 'configs'
}
]
}),
}
其他
迁移改造遇到的问题还是比较多的,查资料的过程中还看到有一些感觉比较常见,但是项目里还没遇到的问题,比如:代码里使用了commonJs
规范的需要用到@originjs/vite-plugin-commonjs
插件处理、全局变量、css module
的处理等等等,后续遇到了再更新。
最后
至此,本项目基本实现了开发环境的vite迁移。测试下来,启动/热更新实现了秒开的提升,解决了等待编译过程带来的内耗🙉。不过,项目还是稍微动了main.js
入口文件,最理想的是完全不改源代码。最后,希望上述内容对大家有帮助,欢迎沟通讨论。
转载自:https://juejin.cn/post/7249933985564000312