前端icon进化史
一个icon对应一个图片资源
最原始的时期,那时候的资源还并不丰富,往往一个页面也就几个普通的icon,这时候只需要让设计的同学把icon设计好,然后前端这边直接把用img标签引入相关icon图片即可
css sprite
后来随着页面越来越复杂,一个页面中的icon开始增多,这时候如果还是用一个icon对应一个图片的方案将会导致页面渲染的时候发出多个http请求,受到浏览器的并发请求数限制,这会导致排队阻塞。为了解决这个问题,前端工程师想到了把所有icon的图片集中到一张图片当中,然后使用css中的background-position对图片进行偏移从而显示特定的icon,这项技术被称为css sprite,中文名为雪碧图。
点击查看【codepen】
data URI
比css sprite走的更为极端的是直接把数据嵌入到html文件当中,这样就能省掉对雪碧图的请求了,这项技术称为data URI。
URI(uniform resource identifier)统一资源标识符,它定义了接受资源的相关协议以及附带内容,如果附带内容为一个位置,那么这个URI也被成为URL(统一资源定位符)。
语法
data:①[<mime type>]②[;charset=<charset>]③[;<encoding>]④,<encoded data>⑤
- data:表示这是一个data URI
- mime type:指明资源的MIME类型
- charset:指明源文本的字符集编码
- encoding:数据的编码方式
- encoded data:编码后的数据
这个数据表示MIME type为<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="">
image/png,数据按base64编码,数据内容即后面那一串。
优劣势
| 优势 | 劣势 |
|---|---|
| 减少http请求 | 数据实际大小大概比源文件大1/3 |
| 避免因外部资源访问受限而导致资源加载失败 | icon本质还是图片,用户无法自定义颜色 |
| 无法独立缓存,每次请求html/css都要携带这些数据 |
iconfont
这项技术是利用CSS3中的 @font-face 能自定义字体的特性,来将对应的icon转换成字体,从而在项目中把icon像字体那样使用
使用方法
第一步:使用 @font-face 将对应icon定义为特定的字体,并且引入对应的字体文件,下面以阿里的iconfont为例:
@font-face {font-family: "iconfont";src: url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.eot?t=1501489744354'); /* IE9*/src: url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.eot?t=1501489744354#iefix') format('embedded-opentype'), /* IE6-IE8 */url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.woff?t=1501489744354') format('woff'), /* chrome, firefox */url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.ttf?t=1501489744354') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.svg?t=1501489744354#iconfont') format('svg'); /* iOS 4.1- */}
上面的代码意思就是把对应的字体文件定义为一个新的 font-family 名为iconfont
第二步:把icon容器的字体设定为iconfont,且在容器中使用特定的字符编码
点击查看【codepen】
阿里还提供了一种叫font-class的引入方法,原理跟上面的类似,使用上更加语义化和方便,这里就不多赘述了。
优劣势
| 优势 | 劣势 |
|---|---|
| icon能通过css实现自定义颜色 | 无法支持多色图标 |
| 能够实现无损缩放 | 不便修改,添加icon需要更改字体文件 |
| 体积较小 |
SVG sprite
目前来说我们对前端的icon有几个要求:
- 无损缩放,不会出现图片那种放大了就变糊的情况
- 支持多色图标,指一个图标中可以有多种颜色,这个iconfont无法满足
- 便于修改,方便开发者随时添加新的icon
上面这几个问题我们都将在SVG sprites中得到解决。
SVG,全称是可缩放矢量图(Scalable Vector Graphics)。普通图像如png,jpg等是基于像素来描述图像,而SVG则是对图像形状的描述,所以它本质上是个文本文件,体积较小,具有无损缩放的特性。
在SVG sprites中,主要是对 svg的symbol 和 use 这两个元素的使用。
- symbol:主要是对页面中用到的svg icon进行集中定义,根据id标识不同的icon,这个元素并不会渲染任何内容,但由于会占据位置所以一般使用
style="display: none"进行隐藏 - use:根据symbol的内容使用
xlink:href属性对对应icon的id进行引用
用法如下:
点击查看【codepen】
以上方法基本实现了我们无损缩放和多色图标两个目标,但是在应用中添加一个图标会非常麻烦,所以我们需要使用webpack来帮我们自动实现从svg文件到svg sprite的转换
利用webpack实现svg sprite
在webpack中我们需要两个loader来帮助我们实现svg sprite的自动化
- svg-sprite-loader:主要用于将项目中的svg文件转换成对应的symbol,并且插入到html文件的body前面
- svgo-loader:主要用于对svg文件的精简,如去掉注释、去掉与渲染无关的一些属性等等
Webpack的配置大体如下:
rules: [{test: /\.svg$/,use: [{loader: "svg-sprite-loader", options: {}},{loader: "svgo-loader"}]}]
然后在我们的js文件或者对应的组件文件中用以下代码批量引入svg文件
function importAll(r) {r.keys().forEach(r);}importAll(require.context("./icons", true, /\.svg$/));
经过上面的引入,webpack经过loader的处理会在body中生成对应的symbol,如下图:
接下来我们只需要使用svg use就可以随意使用我们引入的icon了。
经过上面的处理,当我们需要增加一个icon,我们只需要在存放icon的文件夹中添加对应的svg,然后webpack自动就能帮我们实现svg引入->svg优化->svg sprite一条龙服务。
至此,我们的三大目标:多色图标、无损缩放以及便于修改全部实现。目前来说这种方案的唯一缺点也只是只支持IE9+,但是这个问题也越来越不是问题了。
参考链接:
https://juejin.im/post/5d79b49551882539aa5ad496
https://juejin.im/entry/5c09d58be51d451d9c171d34
https://aotu.io/notes/2016/07/09/SVG-Symbol-component-practice/index.html
https://aotu.io/notes/2018/11/23/SVG_vs_Image_vs_iconfont/index.html
