前言:为了方便使用,一般会有很多图标,我们将它封装成组件,方便复用。并且基于前面我们进行更新一些vite配置,并测试跑通mock,方便后面使用。
一、更新vite配置(vite.congid.js)
主要是加入设置@通配符,加入mock的声明,完整如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default defineConfig({
// 配置路径别名
resolve: {
alias: {
"@":path.resolve(__dirname,'src')
}
},
plugins: [
vue(),
// 配置mock服务
viteMockServe({
// 本地开发时启用mock
localEnabled: true,
// mock文件所在目录
mockPath: './mock/'
}),
createSvgIconsPlugin({
// 指定SVG图标存放目录(使用@别名的话可以写成 '@/icons/svg',但这里需要绝对路径)
iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
// 指定symbolId格式(与SvgIcon组件中的iconName对应)
symbolId: 'icon-[dir]-[name]',
// 配置SVGO优化
svgoOptions: {
multipass: true,
plugins: [
{
name: 'removeAttrs',
params: {
attrs: ['fill', 'fill-rule']
}
}
]
}
})
]
})
二、封装SVGICON渲染组件
# 安装处理SVG的核心插件和优化工具
npm install vite-plugin-svg-icons svgo --save-dev
<template>
<!-- 外部 SVG 图标(通过 URL 引入) -->
<div v-if="isexternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<!-- 内部 SVG 图标(使用雪碧图) -->
<!-- aria-hidden="true":告诉屏幕阅读器该 SVG 图标是装饰性的,无需朗读,提升可访问性。 -->
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script setup>
import { computed } from 'vue'
import { isExternal } from '@/utils/validate'
// 定义组件接收的 props
const props = defineProps({
// 图标标识(内部图标为文件名,外部图标为URL)
iconClass: {
type: String,
required: true
},
// 额外的CSS类名(用于自定义图标样式)
className: {
type: String,
default: ''
}
})
// 判断是否为外部图标
const isexternal = computed(() => {
return isExternal(props.iconClass)
})
// 内部图标在雪碧图中的ID(格式:#icon-文件名)
const iconName = computed(() => {
return `#icon-${props.iconClass}`
})
// 拼接SVG的class类名(基础类+自定义类)
const svgClass = computed(() => {
return props.className ? `svg-icon ${props.className}` : 'svg-icon'
})
// 外部图标的样式(通过CSS mask显示)
const styleExternalIcon = computed(() => {
return {
mask: `url(${props.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%`
}
})
</script>
<style scoped>
/* 内部SVG图标的基础样式 */
.svg-icon {
width: 1em; /* 宽度为1个字体大小单位,方便通过font-size控制图标大小 */
height: 1em; /* 高度与宽度一致,保证图标不变形 */
vertical-align: -0.15em; /* 微调垂直对齐,解决图标与文字 baseline 不一致问题 */
fill: currentColor; /* 填充色继承父元素的color属性,方便通过color控制图标颜色 */
overflow: hidden;
}
/* 外部SVG图标的样式 */
.svg-external-icon {
background-color: currentColor; /* 背景色继承父元素的color,作为图标的颜色 */
mask-size: cover!important; /* 保证遮罩图片(外部SVG)完全覆盖元素 */
display: inline-block; /* 使元素按行内块级显示,避免占满整行 */
}
</style>
在src下开一个utils文件夹用来存放校验代码,src/utils/validate.js(里面包含了isExternal 校验方法)
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
测试使用该组件:
<script setup>
import SvgIcon from '@/components/SvgIcon/index.vue'
</script>
<template>
<SvgIcon icon-class="edit" />
</template>
<style scoped>
</style>
注意:存放的svg是在这里,前面vite.config.js里面的路径也是对应的,不然无法识别,使用时,文件名称一定要对应好。
createSvgIconsPlugin({
// 指定SVG图标存放目录(使用@别名的话可以写成 '@/icons/svg',但这里需要绝对路径)
iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
// 指定symbolId格式(与SvgIcon组件中的iconName对应)
symbolId: 'icon-[dir]-[name]',
// 配置SVGO优化
svgoOptions: {
multipass: true,
plugins: [
{
name: 'removeAttrs',
params: {
attrs: ['fill', 'fill-rule']
}
}
]
}
})
三、测试mcok
记得要开一个mock文件,单独存放mock的配置。我是直接在src下创建的,即src/mock。
在mock下创建一个user.js尝试跑通功能,确保能够正常使用,后续会对这些模拟请求的操作进行模块化。
export default [
{
// 请求路径
url: '/api/login',
// 请求方法
method: 'post',
// 响应数据
response: ({ body }) => {
// 模拟验证用户名密码
if (body.username === 'admin' && body.password === '123456') {
return {
code: 200,
message: '登录成功',
data: {
token: 'mock-token-123456',
userInfo: {
id: 1,
name: '管理员'
}
}
}
} else {
return {
code: 401,
message: '用户名或密码错误'
}
}
}
}
]
在任意页面进行挂载时,尝试请求,看控制台有没有输出内容,如果有获取到数据则能够正常使用。
<script setup>
import axios from 'axios'
import { onMounted } from 'vue'
// 调用登录接口
async function login() {
const res = await axios.post('/api/login', {
username: 'admin',
password: '123456'
})
console.log(res.data)
}
onMounted(() => {
login()
})
</script>