在前端项目中,会用到很多小图标,这些图标很小,但是数量会很多,比如导航、按钮等等,其中很多在不同项目还会重复用到。

使用方式

直接使用图片

最简单的图片使用方式,就是UI给出的图片,在代码中使用img标签,或者background属性来引用。

引用的图片可以维护在项目本地,也可以放到cdn。

雪碧图

直接使用图片,会有很多并发的网络请求,因此可以讲这些小图标合并成一张大图,通过background-position定位来展示不同的图标。这样多个请求合并成一个,优化了性能,但是使用和维护起来麻烦。

关于雪碧图的维护,我们可以通过插件在编译阶段解决,自动生成雪碧图,并且生成css文件,开发的时候,按照文件名的class命名去引用。

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
34
35
36
37
38
39
//webpack.config.js
var path = require('path');

var SpritesmithPlugin = require('webpack-spritesmith');

module.exports = {
// ...
module: {
rules: [
{test: /\.styl$/, use: [
'style-loader',
'css-loader',
'stylus-loader'
]},
{test: /\.png$/, use: [
'file-loader?name=i/[hash].[ext]'
]}
]
},
resolve: {
modules: ["node_modules", "spritesmith-generated"]
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl')
},
apiOptions: {
cssImageRef: "~sprite.png"
}
})
]
// ...
};
1
2
3
4
5
6
7
//style.styl
@import '~sprite.styl'

.close-button
sprite($close)
.open-button
sprite($open)

转base64

相对于雪碧图,在编译阶段将小图标直接转成base64更加方便。虽然图片转成base64体积会增大,不过只是一些小图标的话可以忽略。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
test: /.(jpg|jpeg|webp|png|svg|gif|woff|woff2|eot|ttf|otf)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[path][name].[ext]',
},
},
],
}

iconfont

雪碧图和base64解决了请求多的问题,但是还是以图片的形式维护,如果遇到图标的颜色发生变化,就需要UI给出新的图片,如果需要动态改变图片颜色,就只能切换图片来实现。对于图标的使用,iconfont是一个更好的方案,将图标转成一个字体文件,然后在使用的时候,就可以像字体一样去修改颜色和大小。

首先需要UI提供svg格式的图标,然后通过iconmoon或者阿里巴巴图标资源库(https://www.iconfont.cn/)上传,生产字体文件,然后下载到项目使用。

下载后,放到项目目录,可以新建一个iconfont文件夹,然后引入iconfont.css。项目中就可以用class名来使用字体图标了。这里一般是本地维护字体文件,当有新的图标时,不能直接使用,而是要重新生成字体文件,在项目中更新,再使用新的class,维护麻烦一些。但是使用阿里巴巴图标资源库,便于一个公司或业务线集中维护图标,方便多个不同项目使用。

另外iconfont除了用class方式外,更推荐symbol方式使用,也就是svg的形式,可以支持多色图标,但是直接写起来复杂一些。

用svgtofont(https://www.npmjs.com/package/svgtofont)这个包,也可以在项目本地维护svg图片,本地生成字体图标。

小程序中也可以使用iconfont,但是需要将iconfont的字体文件转换成base64。阿里巴巴图标资源库可以直接生成base64格式的字体文件。也可以将ttf文件在这个网站进行转换:https://transfonter.org/。这里我基于svgtofont开发了一个svg转base64格式iconfont的库:https://www.npmjs.com/package/miniapp-svg2iconfont,方便小程序项目本地维护图标。

svg-sprite-loader

这个其实就是iconfont的symbol方式,不过是在项目本地管理svg图片。然后在vue和react项目中,可以通过封装组件,简化svg的写法。

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
const path = require('path') // 导入模块

// 获取路径
function resolve (dir) {
return path.join(__dirname, dir)
}

module.exports = defineConfig({
...,

chainWebpack: config => {
config.module
.rule('svg')
.exclude.add(resolve('src/icons')) // 这里是svg文件路径
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
},

...,

})
1
2
3
4
5
6
7
8
9
10
import SvgIcon from '@/components/SvgIcon' // 封装的svgIon组件


export default (vue) => {
vue.component('svg-icon', SvgIcon)
// 统一批量引入
const req = require.context('./svg', false, /\.svg$/) // ./svg -> svg文件所在路径
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon"
v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>

<script>
// 判断是否是svg文件链接
import { isExternal } from '@/utils/methods.js'

export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal () {
return isExternal(this.iconClass)
},
iconName () {
return `#icon-${this.iconClass}`
},
svgClass () {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon () {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>

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

.svg-external-icon {
background-color: currentColor;
mask-size: cover !important;
display: inline-block;
}
</style>

这样既有了svg的优点,可以像iconfont方案一样修改大小颜色,并且svg支持的属性更丰富。再一个svg本地管理的话,更便于随时维护,并且svg本身就是代码形式,开发可以编辑微调。

小程序暂不支持svg标签的使用,只能把svg文件在image标签传入使用,因此这个方法在小程序不适用。

维护方式对比

本地图片 cdn图片 阿里巴巴图标资源库
优点 维护方便 代码体积小 项目间共用
缺点 每个项目独立维护 维护麻烦 增加单个图片维护麻烦

综合分析