听说过雪碧图吗?雪碧图是根据 CSS Sprite 音译过来的,就是将很多很多的小图标放在一张图片上,就称之为雪碧图。然后在使用时切换不同的位置而实现展示需要的图片。这个是一样的原理,所以也可以叫做svg雪碧图,即,将多个symbol
标签连接在一起,放在一个svg
标签中,每个symbol
元素都有一个ID,我们调用的时候只需要使用那个ID,即可准确的显示我们需要的svg图标。
如想详细了解,可以看张鑫旭大佬写的这篇文章 => 传送门
在封装组件之前,让我们先来了解两个插件:
这个插件是用来生成上面所述Svg雪碧图的,可以将加载的svg图片拼接成雪碧图,放到页面中,其它地方通过复用
这个插件是用来优化svg代码的,可以通过删除一些冗余的svg代码来达到减小文件体积的目的。不过这个东西大部分是被用来跟svg-sprite-loader配合,实现自定义颜色大小等操作的。因为一般情况下,svg文件直接被img
标签引用的话,是不能改变颜色的。因为文件里会存在初始色,也就是path
标签上的fill
属性不为空,所以无论怎么在css里更改颜色都无济于事。而svgo就可以通过删除fill属性,实现在css中自定义颜色。
了解过这两个插件之后,把两个插件安装进项目:
npm i svg-sprite-loader svgo-loader --save-dev
然后去网站上下载一些svg图标(这里推荐阿里巴巴iconfont矢量图标库),并保存在一个你想保存的文件夹中,这里推荐src/assets/icons/svg
。
最关键的地方来了,也就是通过配置webpack,来让svg-sprite-loader和svgo-loader接管项目中svg文件的解析。关于这个步骤,网上的资料有很多,但是大部分我用过之后都不凑效。于是我不停的摸爬滚打,终于弄出了可行的方案 (该方案仅仅是可行,但并不一定是最完美的)。不过在接管之后,也要注意恢复一下其他普通svg图标的解析loader。
新建一个文件,命名为svgo.config.js
(推荐放在src/assets/icons
目录下)并将下面的内容拷贝进去:
module.exports = {
plugins: [
{
name: 'removeAttrs',
params: {
attrs: ['fill', 'fill-rule']
}
}
]
}
这个文件是用来配置svgo-loader的,详细配置请参考svgo-loader的ReaderMe文档。
修改vue.config.js
,将下面的代码加入chainWebpack中,并重启项目:
// 配置 svg-sprite-loader与svgo-loader
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule
.test(/\.svg$/)
.include.add(path.resolve('src/assets/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({ symbolId: 'icon-[name]' })
.end()
.use('svgo-loader')
.loader('svgo-loader')
.options({ configFile: '../svgo.config.js' })
.end()
// 配置正常svg,这样写完以后,普通的svg图片就可以放在/public/imgs目录下了
const normalSvgRule = config.module.rule('normal_svg')
normalSvgRule
.test(/\.(svg)(\?.*)?$/)
.include.add(path.resolve('public/imgs'))
.end()
.use('file-loader')
.loader(path.resolve('node_modules/file-loader/dist/cjs.js'))
// 这里要注意一下,如果你并没有配置assetsDir属性,那么下面的路径应该是:img/[name].[hash:8].[ext]
.options({ name: 'static/img/[name].[hash:8].[ext]' })
.end()
重启之后,我们需要使用require,将文件全部引入。新建index.ts
,推荐放在src/assets/icons
目录下:
const svgFiles = require.context('./svg', false, /\.svg$/)
svgFiles.keys().map(svgFiles)
然后去main.ts
中引用一下上一步写好的文件,我们在浏览器的开发者工具中,就会看到这样的Svg Sprite了:
可以看到,我的svg文件夹下的四个svg文件,已经连在一起,放在了一个svg
标签下,并且,每个symbol
的id,都是icon-
加上文件名。
这个就直接贴代码吧,没啥好说的:
<template>
<svg
class="svgIcon"
:class="[className, { pointer }]"
:style="{ '--icon-color': color, '--icon-size': variables[iconSize] }"
>
<use :xlink:href="iconName" />
svg>
template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import variables from '@/assets/styles/variables.scss'
export default defineComponent({
name: 'SvgIcon',
props: {
name: {
type: String,
required: true
},
color: String,
className: String,
size: String,
pointer: Boolean
},
setup(props) {
const iconName = computed(() => `#icon-${props.name}`)
const iconSize = computed(() => `SIZE_${props.size?.toUpperCase()}`)
return {
iconName,
iconSize,
variables
}
}
})
script>
<style lang="scss">
.svgIcon {
width: 1em;
height: 1em;
overflow: hidden;
fill: var(--icon-color);
font-size: var(--icon-size);
&.pointer {
cursor: pointer;
}
}
style>
记得全局注册一下这个组件,然后使用的时候就直接这样写
,也就是在name属性中写入svg文件名。然后注意一下,我这里的size属性的值只能使用我预定义好的sass变量,你也可以改成直接像12px, 1.5em这样使用::style="{ '--icon-color': color, '--icon-size': size }"
下一篇预告:vue3.0+ts+element-plus多页签应用模板:侧边导航菜单(上)