原文地址: 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