前言
最近维护一个vue3项目,svg文件要么使用img图片导入,要不之间写在代码里面,感觉非常不优雅,img加载过多也会使得网站体验非常不友好,于是打算把svg文件加载封装成一个组件,提升使用体验。
封装SvgIcon
1. SvgIcon.vue组件封装
xml
<template>
<svg
class="svg-icon"
:class="className"
aria-hidden="true"
:width="width"
:height="height"
>
<use :href="iconName" />
</svg>
</template>
<script lang='ts' setup>
import { computed } from 'vue';
const props = defineProps({
icon: { // svg图片是啥子名字,这个icon就传递啥子名字,例如"arrowDown \ arrowUp"
type: String,
required: true
},
// 图标类名
className: {
type: String,
default: ''
},
width: {
type: [Number, String],
default: ""
},
height: {
type: [Number, String],
default: ""
}
});
// 项目内部图标,就相当于是一个名称
const iconName = computed(() => `#icon-${props.icon}`);
</script>
<style lang='scss' scoped>
.svg-icon {
width: 1em;
height: 1em;
fill: currentColor; // 继承文本颜色
}
</style>
组件就是封装了一个svg标签,加几个传参,很简单的样子,让我们一一解读下。
1.1 icon传参有啥作用?
icon传参为svg文件名称,#icon-${props.icon} 是一个svg的唯一ID,取"icon-"加svg文件名组合的字符串来生成这个ID,只有拿到了这个ID,我们的use标签的href才能将SVG文件复制到当前封装的这个组件所在的位置里。
1.2 组件里面的use标签有啥用呢 ?
<use>
元素从 SVG 文档中获取节点,并将它们复制到其他地方。其效果与将这些节点深度克隆到一个不可导出的 DOM 中,然后粘贴到 use
元素所在的位置相同,这与克隆的模版元素类似。 我们参考如下MDN给的示例,svg中circle有一个id, 我们想复制circle标签到其他两个use标签拿,直接用use的herf属性指向这个id, 即href="#myCircle"就能拿到复制的circle标签了!
ini
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<circle id="myCircle" cx="5" cy="5" r="4" stroke="blue" />
<use href="#myCircle" x="10" fill="blue" />
<use href="#myCircle" x="20" fill="white" stroke="red" />
</svg>
1.3 样式设置
width: 1em; height: 1em;
:使图标大小与父元素的字体大小一致fill: currentColor;
:使图标颜色继承父元素的文本颜色
2 将SvgIcon注册为一个全局组件
javascript
import SvgIcon from './index.vue';
const svgRequire = require.context('@assets/svgIcons', false, /.svg$/);
svgRequire.keys().forEach((svgIcon) => {
svgRequire(svgIcon);
});
// 完成全局注册,这里传入的是main.js中创建的app实例
export default (app) => {
app.component('svg-icon', SvgIcon);
};
2.1. 为啥要使用require.context?
require.context
是 Webpack 提供的一个功能,用于在编译时动态加载模块。它的作用是在指定目录中匹配文件,并生成一个上下文模块,从而可以在代码中动态引入这些文件- 这里的作用是将svgIcons目录下的文件全部加载到项目中, 如果使用
svg-sprite-loader
,这些 SVG 文件会被打包成 SVG Sprite,并通过<use>
标签引用。如何配置svg-sprite-loader
,参考如下
3. svg-sprite-loader插件配置
css
{
test: /\.svg/,
include: path.resolve(__dirname, '../src/assets/svgIcons'),
use: {
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]', // 生成唯一的 symbolId
}
}
}
- 使用include过滤了目录
- symbolId名称是svg文件的唯一标识,和上文的SvgIcon.vue组件的传参icon属性一一对应,不然use的href属性在Svg雪碧图中找不到对应的svg标签模块了!!!
- svg-sprite-loader处理过后的svg文件会被打包成如下类似结果,注意id属性就是我们注入的symbolId
xml
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="icon-icon1" viewBox="0 0 24 24">
<!-- icon1.svg 的内容 -->
</symbol>
<symbol id="icon-icon2" viewBox="0 0 24 24">
<!-- icon2.svg 的内容 -->
</symbol>
<symbol id="icon-icon3" viewBox="0 0 24 24">
<!-- icon3.svg 的内容 -->
</symbol>
</svg>
总结
完结撒花 ~ 如有不足之处,欢迎指出!日有所积,终有所获!