背景
同一天新旧项目一起维护,出现反复使用nvm切换node版本,启动服务,打包等操作,该过程消耗过多的时间,因此决定升级该项目构建工具改用Vite对齐新项目,同时将Vue2.6版本升级2.7,支持Vue3语法编码。
-
旧项目(vue2+webpack3),依赖node14版本。
-
新项目Vite+vue3,依赖node18+(或20+)版本。
项目情况
- 项目阶段:测试修复bug阶段
- 前端投入:1人(无多人协同合并代码冲突风险)
投入资源和风险可控,具备升级条件。
升级前后对比
- 启动项目极快,大幅度提升开发模式体验
- 打包时间低于预期。
备注:打包2种模式,terser模式,打包最慢,压缩率最高,支持移除日志
| 构建工具 | 启动时间 | 打包时间 | 压缩zip | ||
|---|---|---|---|---|---|
| webpack3 | 30秒 | 64秒 | 2.21M | ||
| vite4 | 1秒 | 49秒(terser) | 1.69M(terser) | ||
| vite5 | 1秒 | 22秒(esbuild) 38秒(terser) | 1.66M(esbuild)1.59M(terser) |
参考资料
参考链接:
vuecli+ vue2.6 升级vite4案例:
zhuanlan.zhihu.com/p/471966642…
Webpack + vue2.7 升级Vite4案例:
(备注:以上案例均为升级Vite4经验,升级Vite5存在差异,例如Vite5已不支持vite-plugin-vue2插件【vue2.6】)
改造事项
Vite版本
选择Vite5(2023-11-16发布), 官方特别说明使用Rollup 4,构建性能的大幅提升。
tdesign-vue
tdesign-vue组件在Vue2.7下使用,版本后需要加上-naruto。
sass替代node-sass
使用sass插件,改动代码最多的点。需要全局替换,深度穿透样式代码写法/deep/改成::v-deep
//旧
/deep/ .xxx{}
//新
::v-deep ...{}
图片资源引入【重点】
分为img和background-image2种方式。
自动替换方法
安装vite-plugin-require-transform,自动替换require成 import。
该方案不能兼容所有场景
1.例如旧代码引入图片不采用require写法
//无法识别
<img src="~@/assets/images/common/folder.svg" />
//能识别
<img :src="require(`@/assets/images/home/star.svg`)" />
2.background-image,需替换成@符号【推荐】或改成相对路径引入
//旧
background: url('~@/assets/images/common/count-card.png');
//新
background: url('@/assets/images/common/count-card.png');
// 或
background: url('../../../../../assets/images/common/count-card.png');
3.最麻烦的场景,数据遍历动态加载图片
<img :src="require(`@/assets/images/home/star-${type}.svg`)" />
不想额外实现功能,就用原始方法,v-if把全部动态的图片都写出来
<img v-if="type === 'air'" :src="require(`@/assets/images/home/star-air.svg`)" />
<img v-if="type === 'water'" :src="require(`@/assets/images/home/star-water.svg`)" />
嫌弃太笨,就推荐官方动态URL方案,封装获取图片地址的参数
人工逐个修改
png格式
js文件内引入,只需要require 改成import。
//旧
require('../assets/images/common/upload.png')
//新 png格式
import uploadImg from '@/assets/images/common/upload.png'
data() {
return {
uploadImg: uploadImg
}
},
svg格式
需要借助插件vite-plugin-svg-icons,单独封装组件
npm i vite-plugin-svg-icons -D
参考文档:www.cnblogs.com/wang7151000…
样式资源
引入方式
旧:
公用样式资源改成在main.js中引入,不再使用@import
import '@/assets/css/common.css';
import '@/assets/fonts/font.css';
scss变量资源(例如组件库主题色配置 全局样式变量)在vite.config.js 中引入
css: {
preprocessorOptions: {
scss: {
// additionalData: `@use "@/styles/element/index.scss" as *;`,
additionalData: "@import 'src/assets/css/variable.scss';"
},
},
},
样式覆盖
Vite打包插入样式资源顺序,把组件库(第三方插件)放到最后,因此在覆盖组件库样式,务必加!important
postcssrc.js
移除旧postcss插件的使用
module.exports = {
"plugins": {
// "postcss-import": {},
// "postcss-url": {},
// "autoprefixer": {}
}
}
首先安装 autoprefixer 和 @vitejs/plugin-legacy 这两个依赖包
npm install autoprefixer @vitejs/plugin-legacy --save-dev
创建一个名为 postcss.config.js 的文件并添加以下内容:
module.exports = {
plugins: [require('autoprefixer')]
}
修改项目vite.config.js 文件,将其中的 plugins 字段更新为以下内容:
import legacy from '@vitejs/plugin-legacy';
export default defineConfig({
// ...
plugins: [
// ...
legacy(),
],
});
jsx语法
尽管已经通过createVuePlugin插件避免常规的jsx语法报错,但还是有特殊场景需要前端修改组件,例如代码中有使用jsx语法自定义内容(最常见就是Tdesign Table 单元格自定义)
{ colKey: 'sort', width: '50px', cell: () => <t-icon name='move' /> },
需要单独在组件的script标签加上lang (可以全局替换)
// 旧
<script>
// 新
<script lang="jsx">
环境配置
webpack使用process.env读取环境配置变量,如果还想保留使用process.env(不建议)
import { defineConfig, loadEnv } from 'vite'
const CWD = process.cwd();
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, CWD);
return {
define: {
'process.env': env,
},
}
})
Vite改用import.meta.env读取,.env配置文件记得变量名需要改用VITE前缀开头
VITE_APP_AUTH_CLIENT_ID = 'dgtools'
VITE_APP_AUTH_SERVE = 'http://192.168.0.666'
VITE_APP_STATE = 'dg/brain'
VITE_APP_CONTEXT_PATH = '/dg/brain/api'
VITE_APP_AUTH_PATH = '/oauth/oauth'
vite.config.js示例
import { defineConfig, loadEnv } from 'vite';
import vue2 from '@vitejs/plugin-vue2';
import { createHtmlPlugin } from 'vite-plugin-html';
import { fileURLToPath, URL } from 'node:url';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, process.cwd(), '');
let date = new Date()
const version = `1.0.0`;
const { VITE_APP_STATE } = env
return {
plugins: [
vue2(
),
createHtmlPlugin({
minify: false, // 是否压缩 HTML
entry: 'src/main.js', // 入口文件路径
template: 'index.html', // 自定义模板路径,如果不设置则使用默认的 index.html
inject: {
data: {
title: '系统标题',
// injectScript: `<link rel="icon" href="/${VITE_APP_STATE}/favicon.ico" />`,
},
},
}),
createSvgIconsPlugin({
// 指定要缓存的文件夹
iconDirs: [path.resolve(process.cwd(), 'src/assets/images/svg')],
// 指定symbolId格式
symbolId: '[name]'
}),
visualizer({
open: false,
})
],
build: {
//https://vitejs.dev/config/build-options#build-minify
// minify: true,
reportCompressedSize: true,
cssCodeSplit: true,
// assetsDir: 'assets',
outDir: 'dist', //
rollupOptions: {
output: {
// 引入文件名的名称 output.manualChunks 否则,它将会根据 chunk 的内容确定。
chunkFileNames: `js/[name]-[hash]-${version}.js`,
// 包的入口文件名称
entryFileNames: `js/[name]-[hash]-${version}.js`,
// 资源文件像 字体,图片等
// assetFileNames: '[ext]/[name]-[hash].[ext]',
manualChunks(id) {
if (id.includes('/src/')) {
return 'components-index'
}
// 把库全部单独分拆出来
if (id.includes('node_modules')) {
// console.log('id----', id)
return id
.toString()
.split('node_modules/')[1]
.split('/')[0]
.toString()
}
}
},
},
minify: 'terser', // 'terser' 相对较慢,但大多数情况下构建后的文件体积更小。'esbuild' 最小化混淆更快但构建后的文件相对更大。
terserOptions: {
compress: {
drop_console: true, // 生产环境去除console
drop_debugger: true, // 生产环境去除debugger
},
}
},
base: VITE_APP_STATE,
server: {
host: '0.0.0.0',
port: '8083',
open: false,
cors: true, // 允许跨域
proxy: {
'/dg/brain/api': {
target: 'http://192.168.0.666',
changeOrigin: true,
rewrite: (path) => path.replace('/dg/brain/api', '/')
},
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
extensions: [
'.js',
'.json',
'.jsx',
'.mjs',
'.ts',
'.tsx',
'.vue',
],
},
css: {
preprocessorOptions: {
scss: {
additionalData: "@import 'src/assets/css/variable.scss';"
},
},
},
}
})
package.json插件示例
{
"name": "yoursystemname",
"version": "1.0.0",
"description": "",
"author": "",
"private": true,
"scripts": {
"dev": "vite --mode development",
"build:test": "vite build --mode test",
"build": "vite build --mode production",
"preview": "vite preview",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"lint": "eslint --ext .js,.vue src test/unit"
},
"dependencies": {
"@bundled-es-modules/axios": "^0.27.2",
"@rollup/plugin-replace": "^5.0.5",
"@vitejs/plugin-vue2": "^2.3.1",
"animate.css": "^4.1.1",
"axios": "^1.6.2",
"echarts": "^5.4.1",
"echarts-stat": "^1.2.0",
"element-ui": "^2.15.6",
"mapbox-gl": "^2.15.0",
"markdown-it": "^13.0.1",
"qs": "^6.11.0",
"swiper": "^8.4.7",
"tdesign-vue": "1.8.3-naruto",
"terser": "^5.26.0",
"vite": "5.3.3",
"vue": "^2.7.0",
"vue-bus": "^1.2.1",
"vue-router": "^3.5.2",
"vue-toastification": "^1.7.14",
"vuedraggable": "^2.24.3",
"vuex": "3.6.2"
},
"devDependencies": {
"eslint": "^8.55.0",
"eslint-plugin-vue": "^9.19.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "~1.32.13",
"unplugin-vue-components": "^0.26.0",
"vite-plugin-html": "^3.2.2",
"vite-plugin-svg-icons": "^2.0.1"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
同名组件覆盖
不同目录下的同名组件,存在覆盖的情况,目前通过import时候起别名来解决。
import BaseTagCustom from "@/pages/dataWorkbench/components/BaseTag.vue"
components: {
BaseTagCustom,
},
结语
负责项目是vue2的前端,如果有兴趣升级Vite,希望该文档可以帮助到你。