SVG vs Image, SVG vs Iconfont

這可能是個別人寫過很多次的話題,但貌似由於兼容性的原因?圖標的顯示還是用着 Iconfont 或者 CSS Sprite 的形式?
希望通過自己新瓶裝舊酒的方式能重新引導一下問題。

SVG vs Image

比方說現在要做下圖這樣的視覺效果:

圖片描述

分析:可能需要三張圖片

  1. 鼠標移入時的背景圖
  2. 漸變色前景圖
  3. 鼠標移入時白色前景圖

獨立圖像

現在對比一下背景圖使用圖片與使用 SVG 格式的體積大小(做圖的時候拿錯顏色了,其他都一樣,能說明道理就行,見諒見諒)

clipboard.png

可以看出,在肉眼感覺差異不大的情況下,WebP 格式體積最小,其次是 SVG,而 PNG 的體積過大。

這個 SVG 是在 Sketch 設計稿中導出來的,源碼包含了很多冗餘無效的代碼,實際上是可以優化的,如下。

內部源碼

clipboard.png

優化後

clipboard.png

優化後大約可以減去 1K 個字符。當然這個需要內聯使用(Inline SVG)

clipboard.png

CSS Sprite

使用 CSS Sprite 的方式可以減少 HTTP 請求,貌似還可以減少總體圖片體積。
這裏用前景圖來對比一下,實際上背景圖和前景圖都可以合成一張 sprite

圖片描述

可以看出,CSS Sprite 的體積比 Inline SVG + CSS 的方式大很多。

SVG vs Image 結論

clipboard.png

綠色部分表示 SVGImage 略勝一籌的地方,黃色部分表示有所欠缺的地方,灰綠色表示差不多。

1、如今已接近 2019 年了,對於 IE9 (2011年) 這種古老的瀏覽器都支持 SVG,所以再過多強調更低的兼容性也沒有什麼意思。
2、Inline SVG 在瀏覽器應該是被渲染成 DOM 節點,所以關於 DOM 節點的性能優化都有必要注意;一個 SVG 圖像可能就會有很多路徑,即 DOM 節點,太多的 DOM 節點必然會影響瀏覽器的渲染性能及內存佔用,而純位圖的渲染方式應該是沒有這方面的顧慮。(DOM 數量影響參考:Google WEB 開發者文檔

綜上結論:

除開復雜圖像,選擇 Inline SVG 或者 <img/> 標籤的方式引入 SVG,會比使用 獨立圖像組合圖像 (CSS sprite) 的方式更好。

SVG vs Iconfont

書寫對比

首先看下 IconfontSVG 圖標的使用方式,來源 阿里 Iconfont 平臺

clipboard.png

很明顯 SVG Sprite 使用起來沒有 Iconfont 方便,需要寫 3 行代碼, 而後者只需要寫 1 行。
當然上面的不是重點,重點是下面的換色與多色支持

換色與多色支持

換色

1、Iconfont 通過 CSS color 可以輕鬆更換圖標顏色。

2、而 SVG Sprite 比較麻煩,SVG Sprite 的代碼原理如下。

// 定義 symbol
<svg>
    <symbol id="icon-arrow-left" viewBox="0 0 1024 1024">
      <path d="M694 ... 44.576-45.952"></path>
    </symbol>
    <symbol id="icon-arrow-right" viewBox="0 0 1024 1024">
      <path d="M693 ... 0-0.48-46.4"></path>
    </symbol>
</svg>

// 使用
<svg><use xlink:href="#icon-arrow-left"/></svg>
<svg><use xlink:href="#icon-arrow-right"/></svg>

渲染出來的 DOM 結構是這樣的:

clipboard.png

渲染在了 Shadow DOM 中(關於 Shadow DOM 的知識可以閱讀下這篇文章這篇),
這樣的 DOM 元素樣式就具有了作用域,外面的 CSSshadow-root 內的元素不會生效,
如果想要更換元素的顏色,需要使用 /deep/ 來穿透添加樣式,如下。

svg /deep/ path {
    fill: red;
}

當然,實際上在只需要在父級元素上添加 fill: red 這樣的 CSS 也能起到同樣的效果,裏面的元素會繼承父級的樣式。

PS: /deep/shadow DOM v0 的寫法,v1 已經把這樣的寫法拋棄了,實際上支持 v1shadow DOM, 父級的樣式可以直接作用在 shadow-root 裏面的元素。

多色支持

1、Iconfont 是不支持多色圖標的。

2、而 SVG Sprite 可以利用 CSS 變量或 shadow DOM 的方式支持多色圖標,shadow DOM 的方式上面已經說明,下面借用他人的文章解釋 CSS 變量實現多色,如下。

clipboard.png

不過使用 CSS 變量或 shadow DOM 的方式兼容性都不好,

  1. CSS 變量:Edge15+
  2. shadow DOM:更差。兼容性列表

3、Inline SVG 可以良好地支持多色及多色變化。

漸變色支持

IconfontSVG Sprite 不支持漸變色。
Inline SVG 支持漸變色,並且兼容性良好。

渲染無抖動

使用 Iconfont,因爲字體文件是異步加載的,所以在字體文件還沒有加載完畢之前,圖標位會留空,加載完畢後纔會顯示出來,這個過程就會出現向下圖(來自 GitHub blog)這樣的抖動,而 SVG Sprite Inline SVG 內聯加載則不會出現這樣的抖動。

圖片描述

當然,Iconfont 也可以內聯加載,不過需要轉換成 base64 同樣式表一起加載,轉換後的文件體積則會變爲原來的 1.3 倍左右
這是由 base64 編碼決定的(編碼知識鏈接)。

字體轉換成 base64 的一個在線工具:https://transfonter.org/

體積較大

這個是 SVG 對比於 Iconfont 的一個不足之處,如下圖。

clipboard.png

Inline SVGSVG Sprite 體積差不多。

開發成本

三者的開發成本都差不多,不過 SVG 的兩種方式都需要前期做些配置,後期開發就會順手很多(單頁應用)。

vue + vue cli 爲例說明 Inline SVG 便捷使用。

1、 配置 Webpack loader:

{
  // 排除需要轉換成 Inline SVG 的目錄
  exclude: [resolve('src/svgicons')],
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  loader: 'url-loader',
  options: {
    limit: 1,
    name: utils.assetsPath('img/[name].[hash:7].[ext]')
  }
},
{
  // 指定特定的目錄用於 Inline SVG
  include: [resolve('src/svgicons')],
  test: /\.svg$/,
  use: [
    // 讀取 SVG 源代碼
    { loader: 'raw-loader' },
    // 精簡優化 SVG 源代碼
    {
      loader: 'svgo-loader',
      options: {
        plugins: [
          { removeTitle: true },
          { removeViewBox: false },
          { removeDimensions: true },
          // ...其他參數
        ]
      }
    }
  ]
}

2、 創建 SvgIcon.vue 組件:

<template>
    <div class="svg-icon">
      <div class="svg-icon-wrapper" v-html="icon"></div>
    </div>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    name: {
      type: String,
      required: true,
    },
  },
  data () {
    return {
      icon: this.getIcon(),
    }
  },
  watch: {
    name () {
      this.icon = this.getIcon()
    },
  },
  methods: {
    getIcon () {
      return require(`@/svgicons/${this.name}.svg`)
    },
  },
}
</script>

<style lang="stylus" scoped>
.svg-icon {
  overflow hidden
  display inline-block
  width 1em
  height 1em
  &-wrapper {
    display flex
    align-items center
    >>> svg {
      width 100%
      height 100%
      fill currentColor
    }
  }
}
</style>

3、使用:

<SvgIcon name="arrow-right" />

SVG vs Iconfont 結論

應該是 Inline SVG vs SVG Sprite vs Iconfont 的結論,如下圖。

clipboard.png

綜上結論

選擇 Inline SVG 或許是一個不錯地選擇去替代 Iconfont 的使用方式。

擴展閱讀

  1. GitHub 網站很早之前已經將圖標的展示方式由 Iconfont 轉成了 Inline SVG, 這一篇文章是他們的描述:
    https://blog.github.com/2016-...
  2. 很早的一篇文章關於兩者的對比:https://css-tricks.com/icon-f...

最後

歡迎各抒己見談論一下對 SVGIconfont 的看法,優缺點,歡迎留言。

然後,本文同步發表於【凹凸實驗室博客】或微信公衆號,歡迎關注我們,麼麼噠。

clipboard.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章