vue3图标终极方案【npm包推荐】vue3-icon-sui(含源码详解)
简介
为彻底实现 vue3 项目图标自由,特开发此 npm包 vue3-icon-sui
,全品类图标,通通支持!
- iconify 图标
- svg 图标
- font-class 图标
安装
npm i vue3-icon-sui -S
使用
按需导入
任意页面中
import myIcon from "vue3-icon-sui";
myIcon 可为任意自定义的组件名称
全局注册
src/main.ts
import myIcon from "vue3-icon-sui";const app = createApp(App);
app.component("myIcon", myIcon);
iconify 图标
- 必要传参 icon – iconify 官网图标的名称,支持翻转 flip
- 水平翻转 “horizontal”
- 垂直翻转 “vertical”
- 水平垂直翻转 “horizontal vertical”
- 在 iconify 官网搜索想要的图标
https://icon-sets.iconify.design/?query=home
2. 页面中使用
传给属性 icon
<myIcon icon="ic:baseline-home" color="red" size="36" />
svg 图标(支持多彩)
- 必要传参 name – 项目的
src/assets/icons/svg
目录中svg 图标的名称
-
从https://www.iconfont.cn/ 中找到喜欢的图标
鼠标悬浮其上时,点击下载
可直接下载svg,将其放入项目的
src/assets/icons/svg
目录中,改名为 nice.svg也可以复制 svg 代码,新建
src/assets/icons/svg/nice.svg
文件,再将 svg 代码粘贴到 nice.svg 中 -
页面中使用
将 svg 图标的名称传给属性 name<myIcon name="nice" size="36" />
效果如下
传入 color 可自定义颜色,但会丧失多彩<myIcon name="nice" size="36" color="red" />
font-class 图标
- 从https://www.iconfont.cn/ 中找到喜欢的图标
鼠标悬浮其上时,点击添加入库
加入已有项目,或新建项目
得到 url
//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css
鼠标悬浮在项目的图标上,可一键复制代码,得到 type
icon-nice
-
页面中使用
必传参数 url 和 type<myIconurl="//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css"type="icon-nice"size="36"color="red"/>
属性
属性名 | 属性值 | 说明 |
---|---|---|
icon | iconify 官网图标的名称,如 “ic:baseline-home” | iconify图标必传 |
flip | 水平翻转 “horizontal” 垂直翻转 “vertical” 水平垂直翻转 “horizontal vertical” | 仅iconify图标支持 |
name | 项目的 src/assets/icons/svg 目录中svg 图标的名称 | svg图标必传 |
rotate | 旋转度数,数值即可,如 90 即顺时针旋转90度 | 所有图标都支持 |
color | 颜色,如 red | 所有图标都支持,但svg的多彩图标会变为纯色 |
size | 大小,数值,如 36 即 36px | 所有图标都支持 |
url | font-class图标的css地址,详见使用范例 | font-class图标必传 |
type | font-class图标的代码,详见使用范例 | font-class图标必传 |
fontFamily | font-class图标的前缀 | 除非在项目设置中进行了修改,否则使用默认的 icon-font 即可 |
font-class 图标自定义fontFamily
通常不建议修改!
假设修改为 myfont ,则页面使用时,fontFamily属性需传入 myfont
<myIconurl="//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css"type="icon-nice"fontFamily="myfont"/>
源码
详解见源码注释
<script setup lang="ts">
// 优先推荐【iconify图标】必要传参 icon ,支持翻转 flip
// 搜索图标 https://icon-sets.iconify.design/// 【svg图标-支持多彩图标】必要传参 name
// 需将svg图标放在 src/assets/icons/svg 目录中,// 【font图标-不支持多彩图标】必要传参 url 和 type
// 整个项目使用多个图标时,只需有一个图标传入 url 即可import { computed, onBeforeMount, ref, watch, onMounted } from "vue";
import { Icon } from "@iconify/vue";// 接收的属性
const props = defineProps({icon: {type: String,},// 水平翻转 "horizontal"// 垂直翻转 "vertical"// 水平垂直翻转 "horizontal vertical"flip: {type: String,},name: {type: String,},// 旋转角度rotate: {type: Number,},// 图标颜色color: {type: String,},// 图标大小size: {type: [Number, String],default: 16,},url: {type: String,default: "//at.alicdn.com/t/c/font_2261937_dg35xe8b86.css",},type: {type: String,},fontFamily: {type: String,default: "iconfont",},
});onBeforeMount(() => {if (props.url) {const existingLink = document.querySelector(`link[href="${props.url}"]`);if (!existingLink) {const link = document.createElement("link");link.href = props.url;link.rel = "stylesheet";document.head.appendChild(link);}}
});const className = computed(() => `${props.fontFamily} ${props.type}`);// 计算样式
const newStyle = computed(() => {const style: Record<string, string | number> = {};if (props.size) {style.width = `${props.size}px`;style.height = `${props.size}px`;}if (props.color) {style.color = props.color;}if (props.rotate) {style.transform = `rotate(${props.rotate}deg)`;}return style;
});// 状态管理
const svgContainer = ref<HTMLDivElement>();
const loading = ref(true);
const error = ref(null);// 加载并渲染SVG的函数
const loadAndRenderSvg = async () => {try {// 重置状态loading.value = true;error.value = null;// 使用动态import导入SVG文件,获取原始内容// 加上 ?raw 后,Vite 会直接将 SVG 文件的内容以纯文本字符串的形式返回const svgUrl = "/src/assets/icons/svg/" + props.name + ".svg";const module = await import(svgUrl + "?raw");const svgContent = module.default;// 清空容器if (svgContainer.value) {(svgContainer.value as any).innerHTML = "";}// 创建临时元素解析SVG内容const tempDiv = document.createElement("div");tempDiv.innerHTML = svgContent;// 获取SVG元素const svgElement = tempDiv.querySelector("svg");if (!svgElement) {throw new Error("导入的文件不是有效的SVG");}// 设置SVG属性svgElement.setAttribute("width", props.size + "px");svgElement.setAttribute("height", props.size + "px");if (props.color) {// 替换SVG颜色replaceSvgFillColor(svgElement, props.color);// svg 图片本身没有 fill 时,添加fillsvgElement.setAttribute("fill", props.color);}// 将SVG元素添加到容器if (svgContainer.value) {svgContainer.value.appendChild(svgElement);}} catch (err: any) {error.value = err.message || `无法加载图标: ${props.name}`;} finally {loading.value = false;}
};/*** 替换SVG元素的fill颜色值* @param svgElement 目标SVG元素* @param newColor 新的颜色值(可以是十六进制、rgb、rgba或颜色名称)* @returns 是否成功替换颜色*/
function replaceSvgFillColor(svgElement: SVGElement,newColor: string
): boolean {if (!svgElement) {return false;}try {// 查找所有带有fill属性的路径元素const pathElements = svgElement.querySelectorAll("path[fill]");if (pathElements.length === 0) {return false;}// 替换每个path元素的fill属性pathElements.forEach((path) => {path.setAttribute("fill", newColor);});return true;} catch (error) {return false;}
}// 监听props变化,重新加载图标
watch(() => [props.name, props.size, props.color],() => {loadAndRenderSvg();}
);// 组件挂载时加载图标
onMounted(() => {if (props.name) {loadAndRenderSvg();}
});
</script><template><Iconv-if="props.icon":icon="props.icon":style="newStyle":flip="props.flip"/><template v-else-if="props.name"><!-- 加载状态 --><div v-if="loading" class="loading">加载中...</div><!-- 错误状态 --><div v-if="error" class="error">图标加载失败: {{ error }}</div><!-- SVG容器 - 动态渲染的SVG将插入到这里 --><div ref="svgContainer" v-else></div></template><iv-else="props.type":class="className":style="{fontSize: props.size + 'px',color: props.color,...newStyle,}"style="display: inline-block"></i>
</template>