「本文已参加好文召集令流动,点击查看:[后端、大前端双赛道投稿,2 万元奖池等你挑战!](https://juejin.cn/post/697868…」
导读 :在产品中适当应用图标,能够让产品更活泼,也更简洁。在前端我的项目中,解决和引入图标都是必不可少的环节。在 Web 产品中引入图标,大抵经验过如下几个阶段:应用独立的图片来引入图标、应用 CSS sprites 技术、应用字体图标(font icons)、应用 SVG(inline SVG/SVG sprites)、在前端视图层框架中封装组件。本文将简略梳理一下图标相干的工作流程的演进,以及咱们在百度设计语言零碎推动过程中相干的一些尝试。
全文 7006 字,预计浏览工夫 14 分钟。
一、应用独立图片
在过来有很长一段时间,前端是通过引入图片来承载图标。在没有 CSS 反对的时代,用 <img>
标签引入图标图片是惟一的可能。
<a href="/contact.html"> <img src="mail.jpg" alt="email"></a>
到了 CSS 反对背景图当前,人们开始应用 background-image
来引入一个个小图片,但实质上没有扭转每个图标都应用独自图片的问题。
显然,这样的形式在有很多图标的网页中将发动很多 HTTP 申请,占用浏览器的并行申请数量,导致整体加载工夫迟缓,体验很差。对于有些鼠标悬浮后切换图标的设计,这种形式还会呈现第一次切换时须要期待图标加载的问题。(然而令人丧气的是,直到现在还有网站仍然保留着这样的形式。)
二、CSS Sprite
起初在大概本世纪初的头几年,人们找到了一种新的技巧:通过将图片合并技术(image sprite)引入前端,将数量泛滥的图标图片进行奇妙拼合,并且在款式中通过 background-position
来通过不同地位匹配不同的图标进行显示。例如:
.toolbtn {background: url(icons.png); display: inline-block; height: 20px; width: 20px;}#btn1 {background-position: -20px 0px;}#btn2 {background-position: -40px 0px;}
尽管这种形式相较于每个小图标一个图片文件,只会发动一次 HTTP 申请,对性能更加敌对,然而仍然有着如下问题:
-
拼合后的图片十分难以保护,须要手动精心调整。尽管也有一些主动生成“雪碧图”的工具,但因为
background-position
这种形式的限度,生成逻辑无奈保障灵便适应各种可能的应用场景。图片来自 https://www.smashingmagazine….
- 当一个我的项目图标很多时,图片会在整体下载完当前才显示,可能会导致一段较长的工夫内所有图标都无奈显示。同时因为昂扬的保护老本,很难做到按需加载图标,往往整站的图标都会全副合并到同一个“雪碧图”中。
- 图标色彩是确定的,无奈在前端依据内容上下文灵便调整图标的色彩。
- 图片尺寸是固定的,进行缩放后很难保障图标的显示成果。
在这个时代,设计师和工程师合作的模式一般来说都是设计师将设计好的图标文件交付给工程师,由工程师来通过图片编辑工具或者一些雪碧图生成器来保护拼合后的图片,效率和可维护性都十分堪忧。
三、字体图标的崛起
因为图标从某种程度上来看能够被视为“象形文字”,所以当 CSS 开始反对 @font-face
引入 web font,人们立即想到了用它来载入、显示图标。从 2012 年至今,提供大量收费图标的 FontAwesome 就获得了很大的胜利(起初开始商业化的 FontAwesome 5 的甚至为他们在 Kickstarter 上筹集到了一百万美金),各种字体图标平台也层出不穷。阿里的 iconfont.cn 平台从多年前开始就曾经成为国内最受欢迎的图标托管、共享、治理平台。能够说字体图标时至今日还是最热门的 web 图标计划之一。
字体图标的原理非常简单,通过占用一些 Unicode 字符编码(通常是私人应用区,U+E000-U+F8FF
、U+F0000-U+FFFFD
以及 U+100000-U+10FFFD
范畴内)并为其绘制字形,同时生成好一堆预约义的图标名 class name,通过 web font 的形式加载资源,通过对应的 class name 来援用图标。因为各个浏览器对 web font 反对的字体格局兼容性有差别,往往须要生成多个格局的字体供浏览器进行选择性加载:
/* iconfont.cn 生成的款式文件大抵如下:*/@font-face {font-family: "iconfont"; src: url('iconfont.eot'); /* IE9 */ src: url('iconfont.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('iconfont.woff2') format('woff2'), url('iconfont.woff') format('woff'), url('iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */}.iconfont {font-family: "iconfont" !important;}.icon-flag:before {content: "\e233";}
在 HTML 中应用:
<i class="icon-flag"></i>
字体图标尽管也很难保护,然而相比“雪碧图”还是有不少显著的劣势:
- 基于轮廓字体格局的字体图标是通过贝塞尔曲线形容的,能够任意伸缩并且放弃显示成果不失真,这在挪动端尤为重要。
- 字体能够轻易地应用 CSS 设置色彩。
但咱们能够看出,这个计划对使用者的工程能力曾经有所要求。尽管在这个时代,少数业内前端团队曾经都有了初步的工程化能力,开始应用诸如 Grunt/Gulp 甚至 webpack 等工具,基于 Node + npm 去定制各自团队的工程化计划了,然而编排每个图标的 Unicode 编码、生成对应的 CSS 代码就曾经有比拟大的工作量,更别说生成这么多格局的字体文件,一般工程师基本无从下手。这也是 iconfont.cn 吸引大量用户的重要起因。重度依赖第三方平台,本人建设老本又比拟高,使得图标的可维护性仍然存在肯定的痛点。
另外,尽管字体图标解决了一些“雪碧图”的体验问题,它也带来了一些新问题:
-
字体文件加载须要工夫,在文件加载实现前,图标是无奈显示的,内容就很容易产生闪动。在某些浏览器下,处于公有应用区的图标在默认字体下甚至会显示为一个方块字符。
图片来自 https://github.blog/2016-02-2…
这一点实际上和“雪碧图”有着很大的共同点。尽管咱们能够应用 data URI 来将资源内联,事实上有很长时间咱们也确实应用过将图片或者字体通过 data URI 编码后内联到 HTML 的形式来防止这个加载的时间差,然而编码自身会减少内容 1/3 左右的尺寸,实际上只能算是一种取舍和斗争。更别说字体图标须要生成如此多格局的字体,内联到 HTML 网页性能将大打折扣。
- 可拜访性问题:对于患有视力阻碍应用读屏器的用户,因为字体图标理论由字符承载,无论字体是否加载结束,读屏器都无奈失常朗诵其内容,在默认的状态下甚至会读出“unpronounceable”这样不合乎预期的内容,能够设想如果一个网页大量应用字体图标却没有一一标注
aria-hidden
这样的语义标记,会对读屏器用户产生多大的困惑。
四、SVG 图标
SVG 天生就带有可伸缩(SVG 中的 S)个性,非常适合用来实现图标。同时,SVG 是文本文件,同时诸多反对矢量编辑的设计工具都反对通过 SVG 导出,设计师能够间接交付给工程师应用,也不再须要生成字体文件,大大缓解了可维护性上的痛点。但如果将它当成图片,通过 <img>
或 CSS background-image
来引入,仅仅有这些劣势还不足以撼动图标字体的位置。
4.1 内联 SVG
SVG 的真正弱小之处在于,当将其内联入 HTML 内容,那么它的文档模型将能够被该页面的 JS/CSS 拜访和操作。这为 web 图标开启了新的篇章:
- 能够通过 CSS 管制图标的色彩甚至具体款式,使得受业务逻辑管制的动画图标成为可能。
- 在显示成果上,字体图标因为实质上被视为文本,将受到浏览器的文字抗锯齿算法的影响,在特定操作系统、浏览器、字体设置下视觉效果可能会不那么“保真”。而 SVG 被视为图片进行渲染,不会受文字抗锯齿算法影响,渲染成果更加原汁原味。
- SVG 内联入 HTML 内容并不需要进行编码,反复的 SVG 内容也是对 gzip 敌对的,对 HTML 加载速度的性能损耗很小。
- 不须要发动资源申请,能够随着 HTML 内容进行流式加载和渲染,不会产生任何闪动的体验问题。
- 图标加载能够做到齐全按需,以后页面没有用到的图标都不会输入。
- SVG 能够通过
<title>
元素标记内容,对读屏器敌对。
相比于通过图片资源加载或者图标字体,只有一个劣势:
- 图标成为 HTML 内容的一部分,不再能在 CSS 中指定须要应用的图标了。当然这一点从咱们的实际中来看,并不形成很大的妨碍。
尽管内联 SVG 有很多劣势,然而在这个阶段,在开发时应用它们却不像字体图标那么简略间接(引入一个 CSS,前端就能任意应用),须要对工程有肯定侵入性的解决。GitHub 在 2016 年全面启用了内联 SVG 的计划,他们的技术栈是 Ruby 的后端渲染,通过服务端脚本定义的 helper 函数来进行图标字体的调用:
<%= octicon(:symbol => "plus") %>
输入:
<svg aria-hidden="true" class="octicon octicon-plus" width="12" height="16" role="img" version="1.1" viewBox="0 0 12 16"> <path d="M12 9H7v5H5V9H0V7h5V2h2v5h5v2z"></path></svg>
4.2 SVG Sprite
因为 SVG 反对一个 <use>
元素,能够从内联的 SVG 中选取特定内容进去作为独立的 SVG 进行显示,所以人们受 CSS sprite 的启发,也设计了一个 SVG sprite 计划。引入整个 SVG sprite 的资源仅须要内联一个 <svg>
元素:
<svg> <defs> <symbol id="shape-icon-1"> <!-- icon paths and shapes --> <symbol> <symbol id="shape-icon-2"> <!-- icon paths and shapes --> <symbol> <!-- etc --> </defs></svg>
应用时:
<svg viewBox="0 0 16 16" class="icon"> <use xlink:href="#shape-icon-1"></use></svg>
同时,也有不少基于 Grunt/Gulp/webpack 的构建计划,来疾速生成 SVG sprite。
这种形式次要的问题在于:
- 不容易按需引入图标。
- 在各个场景应用时比拟繁琐。
五、前端组件框架的时代
终于到了咱们当初所处的时代,这是一个 web 端渲染逻辑被移到前端,前端工程方向被组件化框架主导的时代。在应用 React/Vue/Angular/Svelte/…… 等各种框架的过程中,咱们曾经习惯于将视图逻辑通过组件进行拆解和复用。那么咱们很天然地就能够通过设计图标组件来对底层计划进行一层封装,裸露给前端更简略间接的 API 来应用图标。要留神的是,这并没有在基本上扭转 web 图标渲染的形式,底层仍然是基于前文提到的各种计划。在不应用这些视图层框架的我的项目中,咱们仍然仰赖应用上述 low-level 的实现来进行开发。
当然,从各方面综合比拟,封装内联 SVG 应该是以后最佳的抉择。上文 GitHub 后端 helper 的计划对应以后前端的技术计划,实际上就是基于内联 SVG 的图标组件。npm 上目前也有很多基于各个组件框架开发的图标组件,包含 FontAwesome 都曾经内置了 SVG、React/Vue 组件等更现代化的计划。
既然体验问题曾经由内联 SVG 失去了比拟好的解决,那么在这个阶段咱们就有更多的精力去更多地思考研发效力、一致性、开发体验的问题了。从咱们在百度外部以往的实际中来看,存在这如下的一些问题:
- 工作流程不足最佳实际,因为长期各个团队有着较为独立的技术演变,应用的 web 图标计划并不对立。
- 整个大体系下跨团队的设计师并没有很好地共享图标资源,存在肯定的反复设计。
- 有图标组件库,然而图标无限,业务须要新增图标时设计师往往还是将图标线下交付给工程师,前端通过一些相似
svg-icon-loader
的计划将图标引入我的项目,但计划往往各不相同。一旦引入这样的流程,相当于给图标在特定我的项目中新增了一个 fork 版本,日后想做设计格调的对立调整就须要业务跟进批改,老本很高。 - 针对 SVG 图标组件,咱们没有一个相似 iconfont.cn 的平台进行流程上的收拢,也没有自动化的代码包导出、公布能力。
现实状况下,咱们心愿达成如下指标:
- 图标设计师保护图标源文件,公布当前没有任何人工干预造成流程分叉,有一个固定的图标库平台提供 single source of truth。
- 每个团队能依据本身技术栈,抉择须要导出的组件实现类型(React/Vue/San/…)。
- 图标组件库中的图标数据会被主动优化、压缩。
- 图标组件库应该是能够追随图标库的数据更新降级的。
目前咱们在推动百度设计语言零碎的过程中,和工程效力团队一起,设计了如下整体计划:
图标平台整体流程
5.1 图标治理平台
这个平台能够视为是一个简略的图标 CMS,能够创立 / 治理图标库,图标设计师负责来在其中增加、治理图标。在实现数据的更新后,能够抉择公布以后图标输入到 API。这个 API 返回图标库中图标的图形数据(SVG 源文件)和元数据,在整个流程中次要有两个消费者:给设计团队应用的 Sketch 插件,以及前端的编译 / 公布服务。咱们容许图标库公布时通过 webhook 配置须要告诉的编译服务,所以有必要的话,不同的应用方也能够抉择本人自定义整套编译公布的流程。
5.2 Sketch 插件
咱们给设计团队提供了联通图标治理平台的 Sketch 插件,设计师能够在插件中疾速搜寻须要的图标进行应用。通过咱们的插件导出在线标注稿后,标注稿上就会主动标注图标在图标平台中的惟一标识符,这也是咱们用来生成图标组件时用的标识符,前端工程师通过它就能间接从图标组件包中引入对应的图标组件。
5.3 优化 / 编译 / 公布服务
这个服务在图标库 API 触发更新时次要做了三件事:
- 优化。从 API 读取图标数据,并且将源文件通过 SVGO 进行初步优化。因为咱们心愿图标组件内联到 HTML 当前能够通过 CSS 灵便批改色彩,所以对于常见的单色图标,咱们须要去除所有硬编码的色彩,在有必要时设置为
currentColor
。在这一步咱们通过svgson
遍历 SVG 元素解决相干逻辑。 - 编译。失去了优化过的图标数据,咱们须要依据他们来生成咱们的图标组件包。在这里咱们提供了多个框架的组件包模板,每个模板中都曾经提供了对应各自框架的图标组件工厂函数,只须要通过脚本在模板中注入图标数据,即可依据平台数据灵便生成各个业务所须要的组件包。
- 公布。依据在 webhook 回调门路中的配置,咱们能够指定须要公布的包的名称,形容等信息。版本号的逻辑也比较简单:
- 删除 / 改名图标:major + 1
- 新增图标:minor + 1
- 批改图标内容:patch + 1
5.4 图标包模板
编译服务对包模板(boilerplate)仅有的约定是:
- 编译服务会在特定目录输入图标数据。
- 编译服务会顺次调用特定的 npm script。
模板提供者须要提供图标组件的具体实现,以及将图标数据转换为前端代码的构建脚本。如果没有非凡的需要,间接应用咱们提供的 React/Vue 等框架下的组件模板,就能够取得高质量的前端图标组件实现了。
通过编译服务公布实现当前,前端工程师只须要晓得:1. 应用的图标来自哪个 npm 包 2. 这个图标叫什么名字,即可疾速在前端我的项目中引入图标。同时,整个流程保障了设计师产出的设计稿、前端实现的统一,并且能够从图标平台中心化地管制降级。
六、总结
在 Web 产品中引入图标咱们前端工程师做过很多摸索,也产出过很多相干的辅助工具来欠缺整个合作流程。在目前组件化开发的大背景下,咱们通过剖析各个计划的优缺点,建设起一套当下的“最佳实际”,缩小了流程中的沟通和容易出错的人工操作,高效地达成了设计和实现的一致性。最初,心愿本文的内容能给大家带来播种,谢谢。
———- END ———-
百度 Geek 说
百度官网技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
欢送各位同学关注