改造动机
公司的项目是基于若伊二开的,当初考虑到vue3还不是很稳定,所以上的vue2版本。这个版本是用webpack构建打包的,vite的开发体验效果很好,尤其是代码更新后的秒开,真是爽到爆炸,对于我们公司的这种中小型项目来说简直太适合了。于是说干就干,进行vite改造。
准备
- Node.js 18 / 20+
- Yarn (强烈推荐使用yarn下包,体验很爽)
- vue2+webpack项目
开始改造
- 新建工程
- npm init
- 下载需要的包
这里注意:vue低于2.7.0的版本需要升级到2.7.0
yarn add vite -D
yarn add vue@2.7.0 -S
- vite配置文件
- 新建vite.config.js文件
import { defineConfig, loadEnv } from "vite";
import path from "path";
import createVitePlugins from "./vite/plugins";
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd());
const { VITE_APP_ENV } = env;
return {
base: VITE_APP_ENV === "production" ? "/" : "/", // 设置项目根目录
plugins: createVitePlugins(env, command === "build"), // vite插件
resolve: {
// 配置别名
alias: {
"~": path.resolve(__dirname, "./"),
"@": path.resolve(__dirname, "./src"),
},
// 配置文件扩展名
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
},
server: {
port: 90, // 设置端口号
host: true, // 设置为true允许外部访问
open: true, // 设置为true自动打开浏览器
},
build: {
outDir: VITE_APP_ENV === "production" ? "dist" : "dist_test", // 打包输出目录
sourcemap: false, // 生成sourceMap文件
commonjsOptions: {
transformMixedEsModules: true, // 解决node_modules中es6模块打包问题
},
rollupOptions: {
output: {
chunkFileNames: "js/[name]-[hash].js", // 引入文件名的名称
entryFileNames: "js/[name]-[hash].js", // 包的入口文件名称
assetFileNames: "[ext]/[name]-[hash].[ext]", // 资源文件像 字体,图片等
manualChunks(id) {
if (id.includes("node_modules")) {
// 让每个插件都打包成独立的文件
return id
.toString()
.split("node_modules/")[1]
.split("/")[0]
.toString();
}
// return "vendor"; // 将所有代码打包到一个文件中,减少http请求
},
},
},
terserOptions: {
compress: {
drop_console: true, // 生产环境自动去除console
drop_debugger: true, // 生产环境自动去除debugger
},
},
},
};
});
- vite插件配置,这里单独开一个文件夹:vite/plugins文件夹,插件在这里面统一管理
import vue from "@vitejs/plugin-vue2";
import vueJsx from "@vitejs/plugin-vue2-jsx";
import { viteCommonjs } from "@originjs/vite-plugin-commonjs"; // 让浏览器支持commonjs语法
import { visualizer } from "rollup-plugin-visualizer"; // 打包分析
import imagemin from "./imagemin";
import createSvgIcon from "./svg-icon";
import createCompression from "./compression";
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue(), vueJsx()];
vitePlugins.push(createSvgIcon(isBuild));
vitePlugins.push(viteCommonjs());
if (isBuild) {
vitePlugins.push(...createCompression(viteEnv));
vitePlugins.push(visualizer({ open: true }));
vitePlugins.push(imagemin());
}
return vitePlugins;
}
import compression from "vite-plugin-compression";
export default function createCompression(env) {
const { VITE_BUILD_COMPRESS } = env;
const plugin = [];
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(",");
if (compressList.includes("gzip")) {
plugin.push(
compression({
ext: ".gz", // 压缩文件后缀
deleteOriginFile: false, // 压缩后是否删除原文件
})
);
}
if (compressList.includes("brotli")) {
plugin.push(
compression({
ext: ".br", // 压缩文件后缀
algorithm: "brotliCompress", // 使用br压缩
deleteOriginFile: false, // 压缩后是否删除原文件
})
);
}
}
return plugin;
}
import viteImagemin from "vite-plugin-imagemin";
export default function imagemin(isBuild) {
return viteImagemin({
disable: !isBuild, // 生产环境才启用
gifsicle: {
// 压缩gif图片
optimizationLevel: 7, // 优化等级
interlaced: false, // 是否隔行扫描
},
optipng: {
// optipng 图片压缩
optimizationLevel: 7, // 优化等级
},
mozjpeg: {
// 压缩jpg图片
quality: 20, // 压缩质量
},
pngquant: {
// 压缩png图片
quality: [0.8, 0.9], // 压缩质量
speed: 4, // 压缩速度
},
svgo: {
// 压缩svg图片
plugins: [
{
name: "removeViewBox", // 移除viewbox
},
{
name: "removeEmptyAttrs", // 移除空属性
active: false, // 默认不启用
},
],
},
});
}
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";
export default function createSvgIcon() {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/assets/icons/svg")],
symbolId: "icon-[dir]-[name]",
svgoOptions: true,
});
}
- 环境配置
- 分环境配置,这里一共配置三个环境,开发(development)、测试(release)、生产(product)
//package.json
{
"name": "webpack2vite",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build --mode production",
"build:release": "vite build --mode release"
},
"dependencies": {
"vue": "^2.7.0",
},
"devDependencies": {
"vite": "^5.4.1",
},
}
- 根目录下添加环境配置文件
# 页面标题
VITE_APP_TITLE = webpack2vite
# 开发环境配置
VITE_APP_ENV = 'development'
# webpack2vite /开发环境
VITE_APP_BASE_API = 'https://****.***.com'
# 页面标题
VITE_APP_TITLE = webpack2vite
# 开发环境配置
VITE_APP_ENV = 'release'
# webpack2vite /测试环境
VITE_APP_BASE_API = 'https://****.***.com'
# 页面标题
VITE_APP_TITLE = webpack2vite
# 开发环境配置
VITE_APP_ENV = 'product'
# webpack2vite /生产环境
VITE_APP_BASE_API = 'https://****.***.com'
- 插件分析
支持vue2
import vue from "@vitejs/plugin-vue2";
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue()];
return vitePlugins;
}
- 支持jsx
如果项目中有用到jsx的话,项目项目文件需要设置lang='jsx',并且引入支持jsx的插件,否则会报错
<script lang="jsx">
export default {
name: "MenuItem",
render(h, context) {
const { icon, title } = context.props;
const vnodes = [];
vnodes.push(<svg-icon icon-class={icon} />);
return vnodes;
},
};
</script>
import vue from "@vitejs/plugin-vue2";
import vueJsx from "@vitejs/plugin-vue2-jsx";
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue(),vueJsx()];
return vitePlugins;
}
- 项目使用svg图标,批量引入svg
vite的svg图标引入插件配置,vite中不支持require.context('./svg', false, /.svg$/)批量引入图标的方式
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";
export default function createSvgIcon() {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/assets/icons/svg")],
symbolId: "icon-[dir]-[name]",
svgoOptions: true,
});
}
svg图标组件
<template>
<svg :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>
全局挂载使用svg
import SvgIcon from "@/components/SvgIcon"; // svg component
Vue.component("svg-icon", SvgIcon);
打包压缩插件配置
deleteOriginFile: false, 该配置请务必设置为false,否则打包压缩后会删除原文件,只剩下gz后缀文件,上线后会报错!
import compression from "vite-plugin-compression";
export default function createCompression(env) {
const { VITE_BUILD_COMPRESS } = env;
const plugin = [];
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(",");
if (compressList.includes("gzip")) {
plugin.push(
compression({
ext: ".gz", // 压缩文件后缀
deleteOriginFile: false, // 压缩后是否删除原文件
})
);
}
if (compressList.includes("brotli")) {
plugin.push(
compression({
ext: ".br", // 压缩文件后缀
algorithm: "brotliCompress", // 使用br压缩
deleteOriginFile: false, // 压缩后是否删除原文件
})
);
}
}
return plugin;
}
图片压缩插件
import viteImagemin from "vite-plugin-imagemin";
export default function imagemin(isBuild) {
return viteImagemin({
disable: !isBuild, // 生产环境才启用
gifsicle: {
// 压缩gif图片
optimizationLevel: 7, // 优化等级
interlaced: false, // 是否隔行扫描
},
optipng: {
// optipng 图片压缩
optimizationLevel: 7, // 优化等级
},
mozjpeg: {
// 压缩jpg图片
quality: 20, // 压缩质量
},
pngquant: {
// 压缩png图片
quality: [0.8, 0.9], // 压缩质量
speed: 4, // 压缩速度
},
svgo: {
// 压缩svg图片
plugins: [
{
name: "removeViewBox", // 移除viewbox
},
{
name: "removeEmptyAttrs", // 移除空属性
active: false, // 默认不启用
},
],
},
});
}
打包分析插件
打包完成会弹出包分析页面
import vue from "@vitejs/plugin-vue2";
import { visualizer } from "rollup-plugin-visualizer"; // 打包分析
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue(), vueJsx()];
if (isBuild) {
vitePlugins.push(visualizer({ open: true })); //open开启关闭分析
}
return vitePlugins;
}
- 文件路径别名配置
import { defineConfig, loadEnv } from "vite";
import path from "path";
export default defineConfig(({ mode, command }) => {
return {
base: "/", // 设置项目根目录
plugins:[], //插件
resolve:{ //配置路径别名
alias: {
"~": path.resolve(__dirname, "./"),
"@": path.resolve(__dirname, "./src"),
},
// 配置文件扩展名,没有的话,会报错Could not resolve "./App"
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
},
server:{ //服务设置
},
build:{ //打包构建设置
}
}
});
- sass样式文件报错处理
1、element-variables.scss文件会报引入错误:
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
改为
$--font-path: "element-ui/lib/theme-chalk/fonts";
@import "element-ui/packages/theme-chalk/src/index";
2、sass变量导出文件报错
$base-menu-color:#bfcbd9;
:export {
menuColor: $base-menu-color;
}
改为
$base-menu-color:#bfcbd9;
:export {
menuColor: $base-menu-color;
}
- baseapi动态获取
process.env.VUE_APP_BASE_API调整为import.meta.env.VITE_APP_BASE_API
- 图片引入
require("../../../assets/logo/logo-xy.png")调整为new URL("../../../assets/logo/logo-xy.png", import.meta.url).href
- 动态路由,批量引入页面引入页面配置
// 匹配views里面所有的.vue文件
const modules = import.meta.glob("@/views/**/*.vue");
export default [
{
path: "",
component: Layout,
hidden: true,
redirect: "index",
children: [
{
path: "index",
component: loadView('/text/index'),
name: "index",
meta: { title: 首页, icon: "dashboard", affix: true },
},
],
},
]
const loadView = (view) => {
let res;
for (const path in modules) {
let dir = "";
dir = path.split("views/")[1].split(".vue")[0];
if (dir === view || `${view}/index` === dir) {
res = () => modules[path]();
}
}
return res;
};
结语
以上vue2配置vite打包,就全部完成了,现在可以愉快的体验vite开发的迅捷