优雅的使用svg图标

一、前言

很久没更新文章了,正好前两天有人问到svg图标的问题,于是便有了此文

为了节省大家的时间,不卖关子,本文主要讲述项目中如何更优雅的使用svg图标,经过配置后支持自动化导入图标,自动化压缩、剔除无关信息

使用方法:

  • 下载svg图标(iconfont或者来自UI的svg图标)
  • 复制到项目路径下
  • 通过svg名称调用

这篇文章主要参考

二、我与图标

这部分是废话,主要讲我这个半吊子前端开发使用图标的过程,不感兴趣的直接看第三部分

2.1 Layui

前端开发总是离不开各种图标。第一次接触图标是layui的图标,对这个国产UI非常有好感,前期的项目基本都是用layui中的图标,无奈jquery的落寞使得layui也受到很大的影响。

2.2 Layui

之后在layui图标库发现这些图标只是在iconfont中精心挑选的图标,也就是阿里开源的iconfont图标库,虽然登陆后可以管理自己的图标,但是项目中使用还不是很优雅,每次添加或者删除图标,都需要删除之前的文件,在下载,替换,还要删除无关的文件。

好处是搜索很方便,反观fontawesome,想找一个图标真的累

2.3 雪碧图

到大三实习时,面试问到了雪碧图,才知道可以把图标放到一张图上,通过定位显示指定图标,第一次看到是网易云官网的图标

这也是早期优化的一个方式,因为早期图标是通过图片显示的,因此雪碧图的出现可以大大减少http请求次数

2.4 SVG

正式工作后才接触到svg这个图标,可以像操作字体一样操作svg图标,还是很方便的

三、正文

3.1 封装图标组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// SVGIcon
<template>
<svg class="svg-icon" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>

<script>
export default {
name: 'icon-svg',
props: {
name: {
type: String,
required: true
}
},
computed: {
iconName() {
return `#icon-${this.name}`;
}
}
};
</script>

<style>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

3.2 全局注册

main.js中全局注册

1
2
3
import SVGIcon from '@/components/SVGIcon'

Vue.component('svg-icon', SVGIcon);

3.3 安装依赖

安装svg-sprite-loader

1
npm install svg-sprite-loader -D

3.4 修改webpack相关loaders

这里要修改处理图片的loader,排除scr/icons下的svg图标

webpack -> modules -> rules

1
2
3
4
5
6
7
8
9
10
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
// 将src/icons使用单独的loaders处理
exclude: [resolve('src/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},

增加一个单独处理svg图标的loader

1
2
3
4
5
6
7
8
9
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
// 只处理src/icons下的svg
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},

3.5 自动化引入

个人习惯将图标单独建一个文件夹,然后在里层建一个index.js和一个svg的文件夹,svg文件夹用来放所有的svg图标,index.js用于自动导入所有svg图标

1
2
3
4
// index.js
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

这三行写的比较精简,如果看不太明白可以看看这篇文章【webpack中require.context的作用】

requireContext还有很多应用场景,如vuex的模块化引入、vue-router模块化引入、组件的批量注册等等

3.6 使用

  • iconfont下载一个图标,拷贝到项目src/icons/svg下,改名为arrow.svg(随意)

  • 引用

    1
    <svg-icon name="arrow"></svg-icon>
  • 查看效果即可

3.7 改进

如果不考虑优化,前面的配置就可以正常使用svg图标

按照上面的方式可以正常使用,但是打包后的svg往往含有大量无关配置,如SVG的文档声明<!DOCTYPE svg .../>以及一些其他编辑器无关信息,因此通过image-webpack-loader删除无关信息

1
$ npm install image-webpack-loader --save-dev

修改之前单独给svg配置的loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
test: /\.svg$/,
// 只处理src/icons下的svg
include: [resolve('src/icons')],
use: [{
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]'
}
}, {
loader: 'image-webpack-loader',
options: {
svgo: {},
disable: process.env.NODE_ENV == 'development' ? true : false
}
}],
}

前后两次打包比较产生的文件,可以发现使用image-webpack-loader产生的svg文件没有无关信息

相关链接

四、总结

经过一天的折腾,感觉优雅了很多,无论是使用,还是打包部署。唯一的缺点,下载的svg可能自带fill属性,无法通过css控制svg的颜色,目前有两种方式解决,可以在配置一个svgo-loader,删除fill属性,另一种就是下载完后手动删除fill属性。对于webpack相关loader配置用的不是很多,查阅相关文档后继续完善这篇文章