svg是一种基于XML格式的图像描述语音,它是矢量图,相比较与canvas,放大时不会失真,相对于图像来说,因为它本就是文本形式存在,尺寸会更小。传统使用方式,我们会将svg复制到需要用到的地方,或者到处import,这样代码不仅不美观,而且维护起来很麻烦,特别时图标多了的时候。接下来我们讲讲怎么以组件的方式使用svg图标(以vue为例react同理)。
实现原理:利用svg的use标签,通过id引用对应的svg图标
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0" aria-hidden="true" id="__SVG_SPRITE_NODE__">
<symbol xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024" id="icon-back">
<path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8c-16.4 12.8-16.4 37.5 0 50.3l450.8 352.1c5.3 4.1 12.9 0.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z" p-id="7787" fill="#999999"></path>
</symbol>
<symbol xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024" id="icon-close">
<path d="M794.843 794.843c-14.001 14.001-36.911 14.001-50.912 0L229.157 280.069c-14.001-14.001-14.001-36.911 0-50.912 14.001-14.001 36.911-14.001 50.912 0l514.774 514.774c14 14.001 14 36.911 0 50.912z" p-id="2135" fill="#4E5B75"></path>
<path d="M794.843 229.157c14.001 14.001 14.001 36.911 0 50.912L280.069 794.843c-14.001 14.001-36.911 14.001-50.912 0-14.001-14.001-14.001-36.911 0-50.912l514.774-514.774c14.001-14 36.911-14 50.912 0z" p-id="2136" fill="#4E5B75"></path>
</symbol>
</svg>
如上这段svg代码,里面包含两个图标,嵌入html页面是不会显示的,当我们需要使用某个图标时(id="icon-close")为例,可以在html页面如下引用
<svg width="20px" height="20px" aria-hidden="true" class="svg-icon #icon-close">
<use xlink:href="#icon-close"></use>
</svg>
这样思路就出来了,首先我们将所有svg文件打包成一个svg标签里,并且每个symbol标签都设置id,这里我们可以通过webpack的svg-sprite-lpader来实现,如下
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons/svgs')],
options: {
symbolId: 'icon-[name]'
}
}
一般我们会将作为图标使用的svg文件全部放在某个文件夹下,然后通过include处理这个文件下的svg文件,为了防止处理其他不要需要通过这个方式使用的svg被处理。
接下来我们要考虑怎么导入所有问svg文件,这里我们使用webpack的require.context来实现
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
Vue.component('svg-icon',SvgIcon)
let req = require.context('./svgs',false,/\.svg$/)
let importAll = r => r.keys().map(r)
importAll(req)
首先我们将SvgIcon注册成全局组件,这样就不用到处import了,下面三行代码就是require.context动态引入svgs下的以.svg结尾的文件,这里我们生成一个局部webpack上下文,即svgs文件夹路径,里面的文件导入基于这个上下文。最后在index.js引入这个文件即可
最后来实现SvgIcon组件
<template>
<svg
class="svg-icon"
:width="w"
:height="h"
:class="iconName"
aria-hidden="true"
v-on="$listeners"
>
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
icon: {
type: String,
required: true
},
w: {
type: [Number, String],
default: '20px'
},
h: {
type: [Number, String],
default: '20px'
}
},
computed: {
iconName() {
return '#icon-' + this.icon
}
}
}
</script>
<style lang="scss">
.svg-icon {
vertical-align: middle;
}
</style>
然后可以在组件中使用
<template>
<div>
<svg-icon icon="close" />
</div>
</template>