演进史
众所周知,当在项目中大量使用图片,随之而来肯定是大量的请求,为了优化所以出现了img sprite
,就是所谓的精灵图(雪碧图),就是将许多的小图标都放到一张图片上,利用css的background-position
来显示不同位置的图标。这也有一个很大的痛点,每每新增或删除一个图标,就需要改动一次图片,造成之前的缓存失效。
font库
学生时代使用的有font Awesome
,用起来也蛮方便,但是图标量很少,常常找不到合适的图标。
(2021.7.25)font Awesome整体网站的风格已经改版了。图标量也增多了不少。
iconfont
阿里做的一个开源图标库,三大特点:原创设计,海量图标,交流协助。可以直接将源代码下载到本地,进行引入,流程较为简单高效。
iconfont食用方式
unicode
unicode是字体在网页端最原始的引用方式,特点是
- 支持按字体的方式去动态调整图标大小, 颜色等等
- 默认情况不支持多色,直接添加多色图标会去色
使用方法:第一步:自定义fontface
@font-face {
font-family: 'moonfont';
src:
url('iconfont.woff2?t=1627183685551') format('woff2'),
url('iconfont.woff?t=1627183685551') format('woff'),
url('iconfont.ttf?t=1627183685551') format('truetype');
}
第二步:定义使用iconfont的样式
.moonfont {
font-family: "moonfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
第三步:挑选相应图标并获取编码
<span class="moonfont">3</span>
fontClass
fontClass主要解决unicode书写不直观,语义不明确的问题。 特点如下:
- 书写直观
- 使用class来定义图标, 所以当替换图标时,只需要更改unicode的引用即可 使用方法:第一步:引入源代码中的css文件
<link rel="stylesheet" href="./iconfont.css">
第二步:挑选类名
<span class="moonfont moonxxx"></span>
symbol
随着某浏览器淡出舞台,svg-icon
使用形式慢慢成为主流。
- 支持多色图标了,不再受单色限制。
- 支持像字体那样通过font-size,color来调整样式。
- 支持 ie9+
- 可利用CSS实现动画。
- 减少HTTP请求。
- 矢量,缩放不失真
- 可以很精细的控制SVG图标的每一部分 使用方法:第一步:引入源代码中的iconfont.js文件
<script src="./iconfont.js"></script>
第二步:加入通用的css代码(只需编写一次)
<style>
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
第三步:挑选相应类名
<svg class="icon" aria-hidden="true">
<use xlink:href="#xxx"></use>
</svg>
还有一个就是 svg 是一个真正的矢量,不管你再怎么的放缩它都不会失真模糊,而且svg可以控制的属性也更加的丰富,也能做出更加生动和复杂的图标。现在ui设计师平时都喜欢使用 sketch 来工作,只要轻松一键就能导出 svg 了,所以 svg 也更受设计师的青睐。Inline SVG vs Icon Fonts 这篇文章详细的比较了 svg
和 icon-font
的优劣,大家可以去看看。PS:这里其实还用到了 SVG Sprite
技术。简单的理解就是类 svg 的似雪碧图,它在一个 svg 之中运用 symbol 标示了一个一个的 svg 图标,这样一个页面中我们遇到同样的 svg 就不用重复再画一个了,直接使用<use xlink:href="#icon-QQ" x="50" y="50" />
就能使用了,具体的细节可以看这篇文章开头的文章 未来必热:SVG Sprite技术介绍,在之后的文章中也会手摸手叫你自己如何制作 SVG Sprite
。
创建icon-component组件
有了图标,接下来就是如何在项目中优雅食用它了。
<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>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
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组件
import IconSvg from '@/components/IconSvg'
//全局注册icon-svg
Vue.component('icon-svg', IconSvg)
//在代码中使用
<icon-svg icon-class="password" />
进一步改造
目前来说还有一个致命的缺点,就是现在所有的svg-sprite
都是通过iconfont的iconfont.js
生成的。
- 首先它是用js来生成的一段代码,所有图标icon都很不直观
这就导致每次增删改查都要整个替换掉js文件。
- 其次他也做不到按需加载,不能根据我们使用了哪些svg动态生成
svg-sprite
- 自定义性差
- 添加不友善
使用svg-sprite-loader
该loader可以将多个svg打包成svg-sprite
。
接下来介绍如何在vue-cli
的基础上进行改造,加入svg-sprite-loader
。
我们发现默认情况下,vue-cli
会使用url-loader
进行处理,会将它放在/img
目录下,所以不能直接引入svg-sprite-loader
,这会引发一些冲突。
最安全有效的方式是使用exclude,include
关键字,指定你的svg需要被svg-sprite-loader
处理。
单这样还是不够优雅, 如果我有100个svg,不可能每个都手动引入,接下来就该require.context
出场了!
自动导入
首先我们创建一个专门放置图标的icon文件夹如:@/src/icon
,将所有的icon放在这个文件夹下。之后就要用到webpack的 require.context,对于这个api,直白的解释如下
require.context("./test", false, /.test.js$/); 这行代码就会去 test 文件夹(不包含子目录)下面的找所有文件名以
.test.js
结尾的文件能被 require 的文件。 更直白的说就是 我们可以通过正则匹配引入相应的文件模块。
require.context有三个参数
- directory:说明需要检索的目录
- useSubdirectories:是否检索子目录
- regExp:匹配文件的正则表达式
了解这些之后,就可以这样引入@/src/icons
下面所有的图标了
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg',false,/\.svg$/)
requireAll(req)
之后我们增删改图标直接修改文件夹下的对于图标就好了,什么都不用管
更进一步优化自己的svg
这时候我们就要使用另一个很好用的东西了-- svgo
SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.
它支持几十种优化项,非常的强大,8k+的star 也足以说明了问题。
详细的操作可以参照 官方文档 张鑫旭大大的文章(没错又是这位大大的文章,或许这就是大佬吧!)本文就不展开了。