手摸手,帶你優雅的使用 icon

前言

本篇文章其實陸陸續續寫了快半年,主體部分寫好了好久了,但因爲種種緣由一直沒有發佈。
首先來講說寫這篇文章的主要初衷是:在作前端後臺項目的時候常常會用到不少 icon 圖標,剛開始還好,但隨着項目的不斷迭代,每次修改添加圖標會變得很麻煩,並且總以爲不夠優雅,就開始琢磨着有啥簡單方便的工做流呢?

演進史

首先咱們來講一下前端 icon 的發展史。javascript

遠古時代
在我剛開始實習時,大部分圖標都是用 img 來實現的。漸漸發現一個頁面的請求資源中圖片 img 佔了大部分,因此爲了優化有了image sprite 就是所謂的雪碧圖,就是將多個圖片合成一個圖片,而後利用 css 的 background-position 定位顯示不一樣的 icon 圖標。但這個也有一個很大的痛點,維護困難。每新增一個圖標,都須要改動原始圖片,還可能不當心出錯影響到前面定位好的圖片,並且一修改雪碧圖,圖片緩存就失效了,長此以往你不知道該怎麼維護了。css

font 庫
後來漸漸地一個項目裏幾乎不會使用任何本地的圖片了,而使用一些 font 庫來實現頁面圖標。常見的如 Font Awesome ,使用起來也很是的方便,但它有一個致命的缺點就是找起來真的很不方便,每次找一個圖標特別的費眼睛,還有就是它的定製性也很是的不友善,它的圖標庫一共有675個圖標,說少也很多,但仍是會經常出現找不到你所須要圖標的狀況。固然對於沒有啥特別 ui 追求的初創公司來講仍是能忍一忍的。但隨着公司的壯大,來了愈來愈多對前端指手畫腳的人,喪心病狂的設計師,他們會說不!這icon這麼醜,這簡直是在侮辱他們高級設計師的稱號啊!不過好在這時候有了iconfonthtml

iconfont
一個阿里爸爸作的開源圖庫,人家還有專門的 github issue(雖然個人一個 issue 半年多了也沒回應/(ㄒoㄒ)/~~),但人家的圖標數量仍是很驚人的,不只有幾百個公司的開源圖標庫,還有各式各樣的小圖標,還支持自定義建立圖標庫,因此無論你是一家創業公司仍是對設計頗有要求的公司,它都能很好的幫助你解決管理圖標的痛點。你想要的基本都有~前端

iconfont 三種使用姿式

unicode

最開始咱們使用了unicode的格式,它主要的特色是
優點vue

  • 兼容性最好,支持ie6+
  • 支持按字體的方式去動態調整圖標大小,顏色等等

劣勢java

  • 不支持多色圖標
  • 在不一樣的設備瀏覽器字體的渲染會略有差異,在不一樣的瀏覽器或系統中對文字的渲染不一樣,其顯示的位置和大小可能會受到font-size、line-height、word-spacing等CSS屬性的影響,並且這種影響調整起來較爲困難

使用方法:
第一步:引入自定義字體 `font-facereact

@font-face {
   font-family: "iconfont";
   src: url('iconfont.eot'); /* IE9*/
   src: url('iconfont.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */
   url('iconfont.woff') format('woff'), /* chrome, firefox */
   url('iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
   url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
 }

第二步:定義使用iconfont的樣式webpack

.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;
}

第三步:挑選相應圖標並獲取字體編碼,應用於頁面git

<i class="iconfont">&#xe604;</i>

效果圖:程序員

其實它的原理也很簡單,就是經過 @font-face 引入自定義字體(其實就是一個字體庫),它裏面規定了&#xe604 這個對應的形狀就長這企鵝樣。其實相似於 '花褲衩',在不一樣字體設定下長得是不一樣的同樣。

不過它的缺點也顯而易見,unicode的書寫不直觀,語意不明確。光看&#xe604;這個unicode你徹底不知道它表明的是什麼意思。這時候就有了 font-class

font-class

與unicode使用方式相比,具備以下特色:

  • 兼容性良好,支持ie8+
  • 相比於unicode語意明確,書寫更直觀。能夠很容易分辨這個icon是什麼。

使用方法:
第一步:拷貝項目下面生成的fontclass代碼:

../font_8d5l8fzk5b87iudi.css

第二步:挑選相應圖標並獲取類名,應用於頁面:

<i class="iconfont icon-xxx"></i>

效果圖:

image.png

它的主要原理實際上是和 unicode 同樣的,它只是多作了一步,將原先&#xe604這種寫法換成了.icon-QQ,它在每一個 class 的 before 屬性中寫了unicode,省去了人爲寫的麻煩。如 .icon-QQ:before { content: "\e604"; }

相對於unicode 它的修改更加的方便與直觀。但也有一個大坑,以前樓主一個項目中用到了兩組font-class 因爲沒有作好命名空間,全部的class都是放在.iconfont 命名空間下的,一上線引起了各類雪崩問題,修改了半天,因此使用font-class必定要注意命名空間的問題。

symbol

隨着萬惡的某某瀏覽器逐漸淡出歷史舞臺,svg-icon 使用形式慢慢成爲主流和推薦的方法。相關文章能夠參考張鑫旭大大的文章將來必熱:SVG Sprite技術介紹

  • 支持多色圖標了,再也不受單色限制。
  • 支持像字體那樣經過font-size,color來調整樣式。
  • 支持 ie9+
  • 可利用CSS實現動畫。
  • 減小HTTP請求。
  • 矢量,縮放不失真
  • 能夠很精細的控制SVG圖標的每一部分

使用方法:
第一步:拷貝項目下面生成的symbol代碼:

引入  ./iconfont.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>

使用svg-icon的好處是我不再用發送woff|eot|ttf| 這些不少個字體庫請求了,我全部的svg均可之內聯在html內。


還有一個就是 svg 是一個真正的矢量,無論你再怎麼的放縮它都不會失真模糊,並且svg能夠控制的屬性也更加的豐富,也能作出更加生動和複雜的圖標。如今ui設計師平時都喜歡使用 sketch 來工做,只要輕鬆一鍵就能導出 svg 了,因此 svg 也更受設計師的青睞。Inline SVG vs Icon Fonts 這篇文章詳細的比較了 svgicon-font的優劣,你們能夠去看看。PS:這裏其實還用到了 SVG Sprite 技術。簡單的理解就是類 svg 的似雪碧圖,它在一個 svg 之中運用 symbol 標示了一個一個的 svg 圖標,這樣一個頁面中咱們遇到一樣的 svg 就不用重複再畫一個了,直接使用<use xlink:href="#icon-QQ" x="50" y="50" /> 就能使用了,具體的細節能夠看這篇文章開頭的文章 將來必熱:SVG Sprite技術介紹,在以後的文章中也會手摸手叫你本身如何製做 SVG Sprite

建立 icon-component 組件

咱們有了圖標,接下來就是如何在本身的項目中優雅的使用它了。
以後的代碼都是基於 vue 的實例(ps: react 也很簡單,原理都是相似的)

//components/Icon-svg
<template>
  <svg class="svg-icon" aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'icon-svg',
  props: {
    iconClass: {
      type: String,
      required: true
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    }
  }
}
</script>

<style>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>
//引入svg組件
import IconSvg from '@/components/IconSvg'

//全局註冊icon-svg
Vue.component('icon-svg', IconSvg)

//在代碼中使用
<icon-svg icon-class="password" />

就這樣簡單封裝了一個 Icon-svg 組件 ,咱們就能夠簡單優雅的在本身的vue項目之中使用圖標了。

進一步改造

但做爲一個有逼格的前端開發,怎能就此知足呢!目前仍是有一個致命的缺點的,就是如今全部的 svg-sprite 都是經過 iconfont 的 iconfont.js 生成的。

  • 首先它是一段用js來生成svg的代碼,全部圖標 icon 都很不直觀

如圖所示
你徹底不知道哪一個圖標名對應什麼圖標,一臉尼克揚問號??? 每次增刪改圖標只能總體js文件一塊兒替換。

  • 其次它也作不到按需加載,不能根據咱們使用了那些 svg 動態的生成 svg-sprite
  • 自定義性差,一般導出的svg包含大量的無用信息,例如編輯器源信息、註釋等。一般包含其它一些不會影響渲染結果或能夠移除的內容。
  • 添加不友善,若是我有一些自定義的svg圖標,該如何和原有的 iconfont 整合到一塊兒呢?目前只能將其也上傳到 iconfont 和原有的圖標放在一個項目庫中,以後再從新下載,很繁瑣。

使用 svg-sprite

接下來咱們就要本身來製做 svg-sprite 了。這裏要使用到 svg-sprite-loader 這個神器了, 它是一個 webpack loader ,能夠將多個 svg 打包成 svg-sprite

咱們來介紹如何在 vue-cli 的基礎上進行改造,加入 svg-sprite-loader

咱們發現vue-cli默認狀況下會使用 url-loader 對svg進行處理,會將它放在/img 目錄下,因此這時候咱們引入svg-sprite-loader 會引起一些衝突。

//默認`vue-cli` 對svg作的處理,正則匹配後綴名爲.svg的文件,匹配成功以後使用 url-loader 進行處理。
 {
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: utils.assetsPath('img/[name].[hash:7].[ext]')
    }
}

解決方案有兩種,最簡單的就是你能夠將 test 的 svg 去掉,這樣就不會對svg作處理了,固然這樣作是很不友善的。

  • 你不能保證你全部的 svg 都是用來當作 icon的,有些真的可能只是用來當作圖片資源的。
  • 不能確保你使用的一些第三方類庫會使用到 svg。

因此最安全合理的作法是使用 webpack 的 excludeinclude ,讓svg-sprite-loader只處理你指定文件夾下面的 svg,url-loaer只處理除此文件夾以外的因此 svg,這樣就完美解決了以前衝突的問題。
代碼以下

這樣配置好了,只要引入svg以後填寫類名就能夠了

import '@/src/icons/qq.svg; //引入圖標

<svg><use xlink:href="#qq" /></svg>  //使用圖標

單這樣仍是很是的不優雅,若是我項目中有一百個 icon,難不成我要手動一個個引入麼! 偷懶是程序員的第一輩子產力!!!

自動導入

首先咱們建立一個專門放置圖標 icon 的文件夾如:@/src/icons,將全部 icon 放在這個文件夾下。
以後咱們就要使用到 webpack 的 require.context。不少人對於 require.context可能比較陌生,直白的解釋就是

require.context("./test", false, /.test.js$/);
這行代碼就會去 test 文件夾(不包含子目錄)下面的找全部文件名以 .test.js 結尾的文件能被 require 的文件。
更直白的說就是 咱們能夠經過正則匹配引入相應的文件模塊。

require.context有三個參數:

  • directory:說明須要檢索的目錄
  • useSubdirectories:是否檢索子目錄
  • regExp: 匹配文件的正則表達式

瞭解這些以後,咱們就能夠這樣寫來自動引入 @/src/icons 下面全部的圖標了

const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
requireAll(req)

以後咱們增刪改圖標直接直接文件夾下對應的圖標就行了,什麼都不用管,就會自動生成 svg symbol了。

更進一步優化本身的svg

首先咱們來看一下 從 阿里iconfont 網站上導出的 svg 長什麼樣?

沒錯雖然 iconfont 網站導出的 svg 內容已經算蠻精簡的了,但你會發現其實仍是與不少無用的信息,形成了沒必要要的冗餘。就連 iconfont 網站導出的 svg 都這樣,更不用說那些更在乎 ui漂不漂亮不懂技術的設計師了(可能)導出的svg了。好在 svg-sprite-loader也考慮到了這點,它目前只會獲取 svg 中 path 的內容,而其它的信息一律不會獲取。生成 svg 以下圖:

但任何你在 path 中產生的冗餘信息它就不會作處理了。如註釋什麼的

這時候咱們就要使用另外一個很好用的東西了-- svgo

SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.

它支持幾十種優化項,很是的強大,8k+的star 也足以說明了問題。

詳細的操做能夠參照 官方文檔 張鑫旭大大的文章(沒錯又是這位大大的文章,或許這就是大佬吧!)本文就不展開了。

寫在最後

上面大概闡述了一下前端項目中 icon 使用的演進史。
總的來講仍是那句話,適合的纔是最好的。就拿以前爭論的選擇 vue react 仍是 angular,我的以爲每一個框架都有本身的特色和適用的業務場景,因此全部不結合業務場景的推薦和討論都是瞎bb。。。如上文其實大概講了五種前端icon的使用場景,第一種Font Awesome不用它並非由於它很差,而是業務場景不適合,若是你團隊沒有專門的設計師或者對 icon 的自定義度不高徹底可使用它,Font Awesome github有五萬多 star,足見社區對它的承認。還好比說,大家項目對低端瀏覽器有較高的適配要求,你還強行要用 svg 做爲圖標 icon,那你真的是存心和本身過不去了。因此全部方案都沒有絕對的優與劣之分,適合本身業務場景,解決本身實際痛點,提升本身開發效率的方案就是好的方案。

佔坑

本文所涉及的技術在 vue-element-admin 中能夠找到完整的實例。
vue-element-admin也發佈了新版本和配套的中文文檔(文檔真的寫的我要吐血了)無論使不使用本項目都推薦一看,應該能對你寫vue的項目有所幫助。歡迎使用和提出不足。
樓主我的免費圈子

相關文章
相關標籤/搜索