Vue3如何引入SVG图标?一篇文章快速学会!我们在开发 Vue 项目的时候会使用一些前端组件库,例如 Element、
1. 前言
我们在开发 Vue 项目的时候会使用一些前端组件库,例如 Element、Ant Design 等。
这些组件库虽然方便,但是也有一些缺点,比如内置的图标太少。
例如我们开发医疗、财务、工程等一些前端项目,内置的图标不能满足我们的需求。所以我们需要引入外部的图标。
我们常常在 Vue 项目中引入 SVG 图标。
2. 效果展示
3. SVG 简介
SVG 指可伸缩矢量图形 (Scalable Vector Graphics)。
SVG 是使用 XML 来描述二维图形和绘图程序的语言。
说白了 SVG 就跟 jpg、png 一样,都是图形。只不过这玩意是用 xml 语言设计开发的矢量图。
因为是矢量图,所以不管放大还是缩小,都不会失真。
我们分别看3个相同名称不同尺寸的 SVG 图标:
我们用 vscode 打开一个 SVG 图标,发现它由很多标签组成:
如果想深入了解 SVG ,大家可以去以下网站学习
https://www.runoob.com/svg/svg-tutorial.html
https://developer.mozilla.org/zh-CN/docs/Web/SVG
4. 下载 SVG 图标
网上可以下载 SVG 图标的网站有很多,这里我强烈推荐阿里巴巴的 iconfont ,因为它有海量免费的图标供大家学习使用。
官网:
https://www.iconfont.cn/
1.选择图标,点击下载按钮
2.选择颜色和尺寸之后,点击下载 SVG 格式
3.添加到购物车,批量下载
我们也可以将要下载的图标添加到购物车,然后批量下载
下载之后,接下来我们需要 在 vue 项目中引入这些图标。
5. Vue3 引入 SVG 图标
前提:使用 Vite 脚手架开发 Vue3 项目。
如果你还不知道如何使用 Vite 开发 Vue3 项目,请看如下文章:
在 Vite 中使用 Vue3 引入 SVG 图标,我们需要借助以下插件:
vite-plugin-svg-icons
vite-plugin-svg-icons 是一个 Vite 插件,它的主要功能是将 SVG 图标转换为 Vue 组件,并自动导入到项目中。
5.1 安装插件
npm i vite-plugin-svg-icons -D
安装之后运行程序如果报这个错误,需要再安装 fast-glob 插件
npm i fast-glob -D
5.2 main.js 中注册插件
import 'virtual:svg-icons-register'
5.3 配置 vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import { resolve } from "path";
const pathSrc = resolve(__dirname, "src");
export default defineConfig({
plugins: [
vue(),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [resolve(pathSrc, "assets/icons")],
// 指定symbolId格式
symbolId: "icon-[dir]-[name]",
}),],
resolve: {
// 设置别名
alias: {
'@': resolve(__dirname, resolve(__dirname, "./src"))
}
},
});
其中最关键的是指定 svg 图标的存放位置:
5.4 封装展示 SVG 图标的 icon 组件
1.:xlink:href 用来绑定图标的名称,名称前要加前缀 icon
2.fill 属性用来设置图标的颜色
<template>
<svg aria-hidden="true" :fill="color" :style="'width:' + size + ';height:' + size">
<use :xlink:href="symbolId" />
</svg>
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
// icon 名字
name: {
type: String,
default: "",
},
// 填充颜色
color: {
type: String,
default: "black",
},
// 大小
size: {
type: String,
default: "1em",
},
});
const symbolId = computed(() => `#icon-${props.name}`);
</script>
5.5 使用组件
<template>
<div class="content">
<SvgIcon name="client" size="10rem" />
<SvgIcon name="client" size="10rem" color="red" />
<SvgIcon name="client" size="10rem" color="green" />
</div>
</template>
<script setup>
import SvgIcon from "@/components/SvgIcon/index.vue";
</script>
<style lang="scss" scoped></style>
6. 批量导入 SVG 图标
1.import.meta.glob 用来动态导入所有 svg 图标
2.获取所有图标的名称
<template>
<div class="content">
<SvgIcon
v-for="(iconName, index) in allIconNames"
:key="index"
:name="iconName"
size="5rem"
/>
</div>
</template>
<script setup>
import SvgIcon from "@/components/SvgIcon/index.vue";
import { onMounted, toRef, ref } from "vue";
const allIconNames = ref([]); // 所有的图标名称集合
onMounted(() => {
loadAllIcons();
});
// 获取所有 icon
const loadAllIcons = () => {
const icons = import.meta.glob("@/assets/icons/*.svg");
for (const icon in icons) {
// 获取 icon 名称
const iconName = icon.split("/src/assets/icons/")[1].split(".")[0];
allIconNames.value.push(iconName);
}
};
</script>
<style lang="scss" scoped></style>
7. 开发 SVG 搜索组件
这里我们使用 element-plus 作为前端组件库。
我们主要用到 el-input、el-popover、el-scrollbar、el-tooltip 组件。
在 components 文件夹下新建 SelectIcon 组件
在开发这个组件之前,我们先想一下流程:
1.首先封装 el-input,prepend 需要用 SvgIcon 展示选中图标,v-model 需要绑定该图标的名称。
2.点击 el-input, 弹出 el-popover,也就是需要给 el-popover 绑定 visible。
3.el-popover上面需要展示搜索框,下面需要展示所有的图标。
// 加载 icon
onMounted(() => {
loadAllIcons();
});
// 获取所有图标
const loadAllIcons = () => {
const icons = import.meta.glob("@/assets/icons/*.svg");
for (const icon in icons) {
const iconName = icon.split("/src/assets/icons/")[1].split(".")[0];
allIconNames.value.push(iconName);
}
filterIconNames.value = allIconNames.value;
};
4.图标太多需要滚动,所以需要 el-scrollbar 组件进行包裹。
5.筛选图标需要根据所有 SVG 的名称是否包含 filterName
// 筛选 icon
const filterIcon = () => {
if (filterValue.value) {
filterIconNames.value = allIconNames.value.filter((iconName) =>
iconName.includes(filterValue.value)
);
} else {
filterIconNames.value = allIconNames.value;
}
};
6.点击图标需要更新父组件绑定的值
update:modelValue 是 v-model 指令的默认事件,用于在组件内部通知父组件更新绑定的值。
const handleSelect = (iconName) => {
emit("update:modelValue", iconName);
visible.value = false;
};
<el-form-item
label="图标:"
prop="icon"
>
<icon-select ref="IconSelectRef" v-model="sysMenu.icon" />
</el-form-item>
7.1 SelectIcon 组件完整代码
<template>
<div class="content">
<el-input
style="width: 100%"
v-model="inputIconValue"
readonly
placeholder="点击选择图标"
@click="visible = !visible"
>
<template #prepend>
<SvgIcon :size="20" :name="inputIconValue" />
</template>
</el-input>
<el-popover
shadow="none"
:visible="visible"
placement="bottom-end"
trigger="click"
width="400"
>
<template #reference>
<div @click="visible = !visible">
<i-ep-caret-top v-show="visible" />
<i-ep-caret-bottom v-show="!visible" />
</div>
</template>
<!-- 下拉选择弹窗 -->
<div>
<el-row :gutter="10">
<el-col :span="18">
<el-input
v-model="filterValue"
placeholder="输入图标名称"
clearable
@input="filterIcon"
/>
</el-col>
<el-col :span="6">
<el-button @click="closeIcon()">关闭</el-button>
</el-col>
</el-row>
<el-divider border-style="dashed" />
<el-scrollbar height="300px">
<div class="icon-list">
<el-tooltip
v-for="(iconName, index) in filterIconNames"
:key="index"
:content="iconName"
placement="bottom"
effect="light"
>
<div class="icon-item" @click="handleSelect(iconName)">
<SvgIcon :name="iconName" />
</div>
</el-tooltip>
</div>
</el-scrollbar>
</div>
</el-popover>
</div>
</template>
<script setup>
import SvgIcon from "@/components/SvgIcon/index.vue";
import { onMounted, toRef, ref } from "vue";
const visible = ref(false); // 弹窗显示状态
const allIconNames = ref([]); // 所有的图标名称集合
const filterIconNames = ref([]); // 筛选之后名称集合
const filterValue = ref(""); // 筛选的值
// 修改父组件关联的值
const emit = defineEmits(["update:modelValue"]);
const props = defineProps({
modelValue: {
type: String,
require: false,
default: "",
},
});
const inputIconValue = toRef(props, "modelValue");
// 加载 icon
onMounted(() => {
loadAllIcons();
});
// 获取所有图标
const loadAllIcons = () => {
const icons = import.meta.glob("@/assets/icons/*.svg");
for (const icon in icons) {
const iconName = icon.split("/src/assets/icons/")[1].split(".")[0];
allIconNames.value.push(iconName);
}
filterIconNames.value = allIconNames.value;
};
// 筛选 icon
const filterIcon = () => {
if (filterValue.value) {
filterIconNames.value = allIconNames.value.filter((iconName) =>
iconName.includes(filterValue.value)
);
} else {
filterIconNames.value = allIconNames.value;
}
};
// 选择 icon
const handleSelect = (iconName) => {
emit("update:modelValue", iconName);
visible.value = false;
};
// 关闭组件
const closeIcon = () => {
visible.value = false;
filterValue.value = "";
filterIconNames.value = allIconNames.value;
};
</script>
<style lang="scss" scoped>
.el-divider--horizontal {
margin: 10px auto !important;
}
.icon-list {
display: flex;
flex-wrap: wrap;
.icon-item {
display: flex;
justify-content: center;
padding: 5px 0px;
margin: 5px;
width: 10%;
cursor: pointer;
border: 1px solid #ccc;
&:hover {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: all 0.2s;
transform: scaleX(1.1);
}
}
}
</style>
8. 项目完整代码
拿到代码之后记得先 npm install 安装相关插件,再 npm run dev 运行。
链接: https://pan.baidu.com/s/10hnexE2mPcoKJbexylPK7g?pwd=1234
提取码: 1234
转载自:https://juejin.cn/post/7408797826019704883