原文地址: Web Icon 实现方案总结。若有描述不妥之处,欢迎指正。
Icon,在界面设计中,具备指代意义的图形符号。在前端开发中,图标每每由 UI 设计给出,而后经前端开发人员在 html 中使用。Icon 的设计和使用在近几年的发展中,也经历了由当初的 img 方案 到现现在的 svg 方案。下文将从 Icon 的发展历程以及到现现在的 svg sprite 技术给出具体的介绍。css
小时候,咱们都是这样在前端应用一个 icon 的:html
<img src="assets/img/index.png" />
,或者直接引用一个远程的图片地址还不曾长大,有人就说了,孩子,你这样不行啊。页面中要是有不少 Icon 的话岂不请求中都是图片请求。听大人一说,当初的本身吓得抓紧百度了一下。有一篇文章《Best Practices for Speeding Up Your Web Site》,也就是如今你们熟知的雅虎前端性能优化准则。第一条:前端
Minimize HTTP Requests (减小/最小化 http 请求数)
显然由于Icon的展现,就致使了页面中那么多的 http 请求,有点不合情理,大人说的很对啊。vue
因而乎,又打开了百度,怎么优雅的使用的icon呢? css sprite ,这是个什么鬼?android
CSS Sprites are a means of combining multiple images into a single image file for use on a website, to help with performance.
( CSS Sprites 是为优化性能而将多个图片合并到一个图片在网站中使用的方式。)
有了 css sprite, 前端在使用时,只请求一次图片,经过 css 的 background-img
、 background-position
属性控制显示 icon。很显然,减小了 http 的请求次数,终于又能够开开心心的玩耍了。webpack
然而还没开心多久,蛋疼的事情来了。项目中其余功能须要添加额外的 icon,UI 只能是从新再搞图,若是不会影响以前 icon的位置还好,如果调整了以前icon 的位置,之前写的css又得无奈的改动。git
若是每次这样的修改都让 UI 给调整,估计 UI 早早的就疯了。还好社区内很快出现了相应的工具来自动化的完成这些工做。Sprity 一个根据相应配置自动合成 sprite 图的工具,在当时也提供了基于Gulp/Grant 的插件。很显然这种技术在目前已经不怎么实用了,Sprity github 上的项目最近一次提交也已是2年前的事情了。《CSS Sprites: What They Are, Why They’re Cool, and How To Use Them》 这篇文章中给出了在当时如何使用工具自动生成 sprite 图片的方法。github
在当时为了优化性能,还有一种技术 Data URIs,它能够将图片编码后内联于样式表中,避免了额外的 http 请求,同时还能避免配置 background-position
。为优化性能引入的这种技术,可能还存在必定的性能问题,具体能够查看这篇文章给出的论述: 《Data URIs》。web
Web Font 的发展得益于 CSS3 的@font-face属性。vue-cli
容许网页开发者为其网页指定在线字体。 经过这种做者自备字体的方式,@font-face 能够消除对用户电脑字体的依赖。
Icon Font 的思想来自于 Web Font,使用字体的方式设计 Icon。其中阿里巴巴开源的图标库 IconFont 应用普遍。下面以 Iconfont 图标库为例,介绍其使用方法。
登陆 IconFont 后能够搜索本身想要的图标并添加至购物车,购物车中的图标能够添加至项目。Iconfont 提供了以项目进行管理图标的功能。项目中的 web 端图标使用方式有三种:Unicode, Font Class, Symbol。
unicode 是字体在网页端最原始的应用方式,特色是:
使用步骤:
第一步:拷贝项目下面生成的 font-face
@font-face {font-family: 'iconfont'; src: url('iconfont.eot'); src: url('iconfont.eot?#iefix') format('embedded-opentype'), url('iconfont.woff') format('woff'), url('iconfont.ttf') format('truetype'), url('iconfont.svg#iconfont') format('svg'); }
第二步:定义使用 iconfont 的样式
.iconfont{ font-family:"iconfont" !important; font-size:16px;font-style:normal; -webkit-font-smoothing: antialiased; -webkit-text-stroke-width: 0.2px; -moz-osx-font-smoothing: grayscale;}
第三步:挑选相应图标并获取字体编码,应用于页面
<i class="iconfont">3</i>
font-class 是 unicode 使用方式的一种变种,主要是解决 unicode 书写不直观,语意不明确的问题。
与 unicode 使用方式相比,具备以下特色:
使用步骤以下:
第一步:拷贝项目下面生成的 fontclass 代码:
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.css
第二步:挑选相应图标并获取类名,应用于页面
<i class="iconfont icon-xxx"></i>
这是一种全新的使用方式,应该说这才是将来的主流,也是平台目前推荐的用法。 这种用法实际上是作了一个svg的集合,与上面两种相比具备以下特色:
使用步骤以下:
第一步:拷贝项目下面生成的 symbol 代码:
//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js
第二步:加入通用 css 代码(引入一次就行):
<style type="text/css"> .icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; } </style>
第三步:挑选相应图标并获取类名,应用于页面:
<svg class="icon" aria-hidden="true"> <use xlink:href="#icon-xxx"></use> </svg>
由于 IconFont 在显示图标方面的缺陷,开发者开始使用 SVG 做为其替代方案展现 Icon。
其中 Inline SVG vs Icon Fonts 一文中给出了详细的 Inline Svg 与 Icon Fonts之间的区别。
Iconfont主要的缺陷:
font-size
,line-height
, word-spacing
等css属性影响,其容器的css样式也会可能影响到该字体icon的位置等。下面列举了项目中使用 SVG 的几种方式,各有优缺点:
早期使用 svg 的一种方式。缺点在于每一个图标都须要保存成一个 svg 文件,使用时单独请求。项目中图标过多的化会带来过多的 http 请求。
顾名思义,将 svg 直接写进 html,这种方法简单暴力,能够减小 http 的请求。
优势: 能够直接使用 class 进行 svg 的样式定制,可控性强
缺点: 复用性差,效率低
css 中直接使用 base64 编码后的 svg
.icon{ background: url(data:text/svg+xml;base64,<base64 encoded data>) }
优势: 不须要额外引用 SVG 文件
缺点:可控性差,没法使用 css 进行样式定制,可能会存在潜在的效率问题
初期最基础的 svg sprite 技术相似于 css sprite,经过 background-position
等属性控制其显示的位置。相似于css sprite, 社区中也出现了对应的工具和在线网站提供生成 svg sprite 的方法。
优势: 减小 http 请求,能够 fallback 到 css sprite
缺点: 可控性差,没法方便的经过css进行控制样式
svg symbols 是定义 svg 引用的一种方式,基于该方式下的 svg sprite 是在传统 svg sprite 上的改进,改进了在 sprite 中获取单一 Icon 的调用方式,以前是根据位置,基于 svg symbol 下的调用是根据引用。
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="circle-cross" viewBox="0 0 32 32"> <title>circle-cross icon</title> <path d="M16 1.333q2.99 0 5.703 1.161t4.677 3.125 3.125 4.677 1.161 5.703-1.161 5.703-3.125 4.677-4.677 3.125-5.703 1.161-5.703-1.161-4.677-3.125-3.125-4.677-1.161-5.703 1.161-5.703 3.125-4.677 4.677-3.125 5.703-1.161zm0 2.667q-2.438 0-4.661.953t-3.828 2.557-2.557 3.828-.953 4.661.953 4.661 2.557 3.828 3.828 2.557 4.661.953 4.661-.953 3.828-2.557 2.557-3.828.953-4.661-.953-4.661-2.557-3.828-3.828-2.557-4.661-.953zm3.771 6.885q.552 0 .948.391t.396.943-.396.948l-2.833 2.833 2.833 2.823q.396.396.396.938 0 .552-.396.943t-.948.391-.938-.385l-2.833-2.823-2.823 2.823q-.385.385-.948.385-.552 0-.943-.385t-.391-.938q0-.563.385-.948l2.833-2.823-2.833-2.833q-.385-.385-.385-.938t.391-.948.943-.396.948.396l2.823 2.833 2.833-2.833q.396-.396.938-.396z"/> </symbol> <symbol id="circle-check" viewBox="0 0 32 32"> <title>circle-check icon</title> <path d="M16 1.333q2.99 0 5.703 1.161t4.677 3.125 3.125 4.677 1.161 5.703-1.161 5.703-3.125 4.677-4.677 3.125-5.703 1.161-5.703-1.161-4.677-3.125-3.125-4.677-1.161-5.703 1.161-5.703 3.125-4.677 4.677-3.125 5.703-1.161zm0 2.667q-2.438 0-4.661.953t-3.828 2.557-2.557 3.828-.953 4.661.953 4.661 2.557 3.828 3.828 2.557 4.661.953 4.661-.953 3.828-2.557 2.557-3.828.953-4.661-.953-4.661-2.557-3.828-3.828-2.557-4.661-.953zm4.49 7.99q.552 0 .943.391t.391.943-.396.948l-5.656 5.656q-.385.385-.938.385-.563 0-.948-.385l-2.833-2.823q-.385-.385-.385-.948 0-.552.391-.943t.943-.391.948.396l1.885 1.885 4.708-4.719q.396-.396.948-.396z"/> </symbol> <!-- .... --> </svg>
每一个Symbol设置一个id做为其引用的名字。
使用方法:
第一步: 将上述 svg 做为 body 的第一个子元素插入。
第二步: 在须要引用 icon 的地方经过 use xlink:href 的方式使用 svg
<svg class="icon"> <use xlink:href="#circle-cross"></use> </svg>
上述基于 Symbol 制做 svg-sprite 的方式,使用起来方便,经过使用 id 引用对应的svg,避免了使用background-position
进行 svg 的引用。即便后期须要从新合并新的 svg-sprite,只须要合并先后对应svg的symbol id 不发生变化,合并先后业务中已经使用的 svg 就不用作任何变化。
在 vue-svg-icon 这个 demo 中详细给出了基于 webpack 的 vue 单页面项目中如何继承 svg-icon 方案的步骤。
第一步:制做svg-sprite:
webpack 中添加 svg-sprite-loader, 并添加以下配置:
{ test: /\.svg$/, include: [resolve('src/components/svg-icon/icons')], use: [ { loader: 'svg-sprite-loader', options: { symbolId: 'icon-[name]' } } ] },
svg-sprite-loader 将咱们引用的指定文件夹下的 svg 制做成 svg sprite 并插入 html 的 body 中。
须要注意,此处咱们应指定文件夹存放咱们项目中全部 svg icon,vue-cli 中还提供了 url-loader 处理 svg,所以咱们应添加以下配置,避免 icon 下的 svg 文件被 url-loader 处理。
{ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', exclude: [resolve('src/components/svg-icon/icons')], // 默认不处理该文件夹的命中的文件 options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } },
经过 exclude 的配置能够避免指定icon文件夹下的svg文件被url-loader处理。
第二步: 封装使用时的 svg component
上面给出了在生成 svg sprite 后,经过 use 使用 svg 的方法。为方便项目中引用,封装 SvgIcon.vue 组件。
<!-- svg-icon 组件,业务组件中直接使用该组件展现 icon --> <template> <svg :class="svgClass" aria-hidden="true"> <use :xlink:href="iconName"></use> </svg> </template> <script> // 引入全部的svg的文件 const requireAll = requireContext => requireContext.keys().map(requireContext); const req = require.context('./icons', false, /\.svg$/); requireAll(req); export default { name: 'svg-icon', props: { iconClass: { type: String, required: true, }, className: { type: String, }, }, computed: { iconName() { return `#icon-${this.iconClass}`; }, svgClass() { if (this.className) { return `svg-icon ${this.className}`; } return 'svg-icon'; }, }, }; </script> <style scoped> .svg-icon { width: 40px; height: 40px; vertical-align: -0.15em; fill: currentColor; overflow: hidden; } </style>
为何要进行 svg 文件精简?
UI同窗经过工具导出的SVG文件一般包含大量冗余且无用的信息,如编辑器元数据,注释,隐藏元素,默认值以及其余能够删除且不影响svg正常渲染的内容。
相似iconmoon.io、Iconfont 都提供了在线精简svg的功能。
在多人协做以及须要频繁改动svg文件的中大型项目中,显然这种依赖手动流程去精简svg的方法已经没法知足快速开发的须要。所以咱们须要在咱们的工做流中集成相似的精简svg的工具。
svgo(svg optimizer) 是一个基于Nodejs的svg文件优化工具,其经过一系列的配置项能够实现定制化的精简svg的需求。
svgo-loader 基于webpack以及svgo的用于优化svg的loader。
使用方式:
webpack.base.conf.js
// 引入svgo的配置文件 const svgoConfig = require('../config/svgo-config.json'); ... { test: /\.svg$/, include: [resolve('src/components/svg-icon/icons')], use: [ { loader: 'svg-sprite-loader', options: { symbolId: 'icon-[name]' } }, { loader: 'svgo-loader', options: svgoConfig, } ] },
svgo-config.json(定义了精简svg的规则)
{ "plugins": [ { "cleanupAttrs": true }, { "cleanupEnableBackground": true }, { "cleanupIDs": true }, { "cleanupListOfValues": true }, { "cleanupNumericValues": true }, { "collapseGroups": true }, { "convertColors": true }, { "convertPathData": true }, { "convertShapeToPath": true }, { "convertStyleToAttrs": true }, { "convertTransform": true }, { "mergePaths": true }, { "removeComments": true }, { "removeDesc": true }, { "removeDimensions": true }, { "removeDoctype": true }, { "removeEditorsNSData": true }, { "removeEmptyAttrs": true }, { "removeEmptyContainers": true }, { "removeEmptyText": true }, { "removeHiddenElems": true }, { "removeMetadata": true }, { "removeNonInheritableGroupAttrs": true }, { "removeRasterImages": true }, { "removeTitle": true }, { "removeUnknownsAndDefaults": true }, { "removeUselessDefs": true }, { "removeUnusedNS": true }, { "removeUselessStrokeAndFill": true }, { "removeAttrs": { "attrs": "fill"} //移除fill属性 }, { "removeXMLProcInst": true }, { "removeStyleElement": true }, { "removeUnknownsAndDefaults": true}, { "sortAttrs": true } ] }
在 web 开发的过程当中,咱们常常会遇到一些状态相关的 icon。好比,当用户 click 或者 hover 时,咱们须要对 icon 的颜色进行相应的变化。采用 Iconfont 方案时,由于其字体的本质,咱们能够直接对字体的颜色使用 css 进行控制。
svg-icon 方案的使用过程当中 svg 的颜色是一种填充色机制。经过fill属性将具体的路径进行颜色填充。如相关路径未指定fill属性,则其继承父元素的color属性进行填充。
所以 svg-icon 方案下状态颜色的变化分为如下两种状况:
基于 svg-sprite 的svg-icon 方案在 14年的时候就已经出现,鉴于当时浏览器兼容性等缘由,并无获得大规模采用。现在随着技术的更新,兼容性显然已经不在是svg-icon应用的阻碍。移动端android 3.x, IE 9+ 均可以进行采用 svg-icon 的 icon 方案。经过调研,发现目前已经应用了 svg-icon 技术方案的有:
任何技术方案的讨论都脱离不了其应用场景。在实际的开发中,由于各类缘由,可能会存在多种icon方案并行的状况,所以在实际的开发过程当中应具体问题具体分析。
参考连接:
symbol
a Good Choice for Icons