切图仔的自我修养——图标管理
同为程序员,在细分工种下,前端和其他方向的一个最大的区别就是除了写代码之外,还要和视觉层面上的图形打交道,对我来说这也是前端最大的魅力,因为我深知图形之美以及其奥妙之处。不过并不是所有前端都会深入图形领域,但对于所有前端来说有一门图形相关的入门课倒是必修的,那就是图标管理了。
图标发展史
前端经过多年的工程化演进,一些细节技术方案也吃到了这波福利,比如图标管理。虽然一直被调侃切图仔,但现在的前端也已经很少切图了,要了解如何摆脱这个称号的话,还是从那个年代开始谈起吧。
img时代
最早期也就是切图仔时代,网页本身就只是用来展示图片和文本信息的。在后端用模板引擎渲染出html
展示文本,同时有一个静态资源文件夹,里面存放着需要的图片,在文本需要的地方直接通过url
引用即可。那时也没有对图片更细节的划分,图标不就是更小的图片嘛。所以图标的产出到应用大致要经过以下的步骤
PhotoShop从原始图中切出需要部分 => 扣除单色背景 => 在
html
中引用
现在回头来看这样的方案当然满是缺点,比如资源请求过多,缩放影响清晰度等等,但直至今日这样的方案依然存在,最大的一个优点就在于简单,灵活。如果整个项目只有少量图标的情况下,也不是不能用。
在html
拿到资源做展示的方案有两种——img
标签加载或者是background
属性加载,由此也衍生出了各种hack
的图标文字居中方案以及CSS Sprites
方案。这些都属于前端入门必知也不是今天的重点就不详细介绍了。
font时代
其实严格来说这并不是一套标准方案,而是前人智慧下利用css
字体规则构思出的hack
方案。首先我们需要了解,网页中的字体究竟是什么。有兴趣的可以随便通过一件字体软件打开一款字体看看,就可以发现,每个文字本身是由矢量图形构成的,这些矢量图形的产出通常是由设计工具ai
,sketch
完成。在字体规则中,每个文字都有一个原始文字的unicode
到对应字体文字的矢量图形映射,所以在html
中我们引入字体文件并声明字体后,不同的文字就可以转化对应字体图形了。
既然有unicode
到矢量图形的映射,那么我们规定一些unicode
映射到指定的图标的规则,不就形成一套完善的图标方案了吗?这套规则的产物就是自定义字体了。初期会有一些代表性的font
库提供给前端使用,作为便捷方案是不错,像bootstrap
一样。但有自定义需求的情况还是比较多的,后来就有了自定义font
解决方案,比较有代表性的项目就是iconfont。
这套方案的好处是明显的,首先就是用到了矢量图形解决了缩放的问题,解决了请求数量的问题,再就是相比于img
各种样式实现,有了一套规范来约束。同时又由于本身是字体,各种字体相关的css
属性都能应用,灵活性足够高。
缺点就在于字体的局限性,只能渲染为单色或者css
支持的渐变色。以及后期维护困难,需要借助外部工具输出相关字体文件,每次更新图标都是挺麻烦的一件事。
svg时代
随着浏览器的演进以及html
的发展,svg
这样的矢量图像格式不用再经过字体的转化,可以直接使用html
标签在浏览器渲染,终于实现了矢量图标的解放,可以更细节的决定其内部构成,由此产生了一系列围绕svg
的工程化方案,是目前最流行的方案,也是本文重点,下面就谈谈就具体细节。
SVG图标解决方案
常规流程
在不做任何优化之前,先了解下如今使用svg
的常规流程。
产出
svg
图标首先我们需要有一个
svg
图标,这一点和font
方案是一致的,通常由设计师在Ai
或Sketch
中制作并导出。常规制作方法会是用path
按步骤绘制,但如果你比较熟悉这些设计工具的话也有各种奇技淫巧生产svg
,比如img
转svg
等。图标文件夹统一管理
和
img
类型的图标无异,通常是新建一个icons
文件夹,内置各种各样的svg
图标。在
html
中使用svg
到了这一步就应该了解
svg
相关的html
标签使用知识了。除了和img
相同的src
引用和background
引用之外,还可以直接用相关标签<path>
等绘制,以及<use>
引用。相比于font
中的svg
,大大拓宽了矢量图形的组合自由度。优化
svg
的样式利用
css
的继承特性,在<svg>
上的许多css
属性设置都可以被内层享受到,除了常规css
属性外,还有svg
特有的属性,比如fill
,stroke
等。
工程优化
可以看到常规流程的后两步,都有各种各样的实现。如果是个人项目当然随你喜欢,但在一个大型工程项目中,还是需要有一套统一的管理方案,目前最推荐的方案是SVG Sprites
。
SVG Sprites
听名字就知道了,这是类似于CSS Sprite
的一项将svg
整合到一起并按统一规范使用的技术。这个技术核心在于svg
的symbol
标签。通常一份SVG Sprites
是由多份symbol
组合而成的,基本结构如下:
<svg>
<symbol id="icon-a"></symbol>
<symbol id="icon-b"></symbol>
<symbol id="icon-c"></symbol>
</svg>
symbol
中就是各个图标的具体实现的声明。需要被使用时,会通过<use>
的xlink:href
属性引用到指定symbol
,再利用shadow dom
渲染到页面。格式如下:
<svg><use xlink:href="path/icons.svg#a"></use></svg>
这样就成功引用到了id
为a
的svg
图标。
当然要我们手动将各种图标集成到icons.svg
中也是件麻烦事,现在iconfont
也提供了SVG Sprites
管理和生成功能,生成的icons.svg
和icons
文件夹共同维护即可。
需要注意的一点是,以上这种通过路径引用其他svg
文件的方式称作外部SVG Sprites
,但由于兼容性的问题,部分浏览器版本不支持外部引用的形式,只支持形如<use xlink:href="#a" />
内联引用,可以使用svg4everybody这个工具做polyfill
,当浏览器不支持svg
外部引用时,其会异步加载svg
源文件并注入到页面中,将引用方式改造为内联。
接入Webpack
统一了规范是很好,但相对的整套体系也变得繁重了。想象一下每次需要新增一个图标,不能直接再扔到icons
文件夹下了,还需要打开iconfont
重新制作icons.svg
文件,再复制到项目中,同时还要注意和源文件的同步问题。想想都觉得麻烦,所以还是需要一份自动化的策略。得益于Webpack
等繁荣的打包工具生态,通过svg-sprite-loader
就能帮助我们完成这件事,大致需要经过如下几个步骤。
- 在
Webpack
配置文件中配置svg-sprite-loader
处理.svg
后缀文件
详细配置就不细说了,需要注意的一点就是,并不是所有svg
文件都是图标,所以最好通过exclude
和include
属性分别对不同类型的svg
文件处理。
- 新建聚合所有
svg
图标的js
导出文件
这一步也是常规操作了,将来需要用到icon
的地方直接引用该文件即可。由于图标通常很多而且路径都统一在icons
文件夹下,可以使用Webpack
提供的语法糖require.context
实现统一引入。
- 配置svgo进一步优化
svg
文件输出
svg-sprite-loader
的配置仅提供了有限的优化功能,因为其本职工作还是生成SVG Sprite
文件,而svgo
是专门为svg
优化而生的,支持更多的优化项。
小结
svg
图标是未来,但其实也只是svg
的一小部分应用而已。其真正强大之处还在于动画方面的应用,当然这又是一个大话题了,之后有计划搞一系列专题文章聊聊。
-- EOF --