svg圖示的webpack優化

熊貓7發表於2019-04-12

svg圖示的webpack優化

前言

最近在做專案的優化升級,因為折騰了一下午的圖示管理,所以就把自己的經驗分享出來,希望可以幫助大家更快的解決自己的需求。

我現在的專案,都是引入iconfont的圖示庫,真是的好用又方便。現在有三種模式,使用方法可以在官網的 使用說明 頁檢視。我們今天主要討論的主要是第三種,也就是svg圖示的優化。

簡單封裝元件

使用說明裡有提及,我們引入官方生成的庫檔案路徑。寫好css樣式,頁面程式碼引用如下:

<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-xxx"></use>
</svg>
複製程式碼

我們簡單的做一個元件封裝如下:

// SvgIcon.vue
<template>
  <svg class="icon">
    <use :xlink:href="iconName"></use>
  </svg>
</template>
<script>
export default {
  props: {
    fontClass: {
      required: true,
      type: String
    },
    className: {
      type: String,
      default: ""
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.fontClass}`;
    }
  }
};
</script>
<style>
.icon {
  width: 40px;
  height: 40px;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

複製程式碼

將該元件註冊為全域性元件。新建資料夾,路徑為src/icons。在其中新建一個index.js檔案。

// icons/index.js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'

Vue.component('svg-icon', SvgIcon)
複製程式碼

main.js中,我們引入這個index.js

import './icons/index.js'
複製程式碼

這樣我們就可以在檔案中隨意使用svg-icon元件了。

使用svg-sprite-loader

現在我們的svg檔案是整體的,每當我們修改的時候,需要在iconfont上生成新的整體檔案。那麼是否能需要用一個icon的時候,就自己手動加一個呢?然後自動打包成一個整體檔案。webpack可以幫我們實現。

首先,我們新建一個資料夾專門來放置單個svg檔案。

svg圖示的webpack優化
如圖,引入了多個從iconfont中下載的彩色圖示svg檔案。我們使用svg-sprite-loader來把所有的單個svg合成為整體的svg雪碧檔案。可以先看vue-cli3中的關於svg的預設配置(通過 vue inspect 命令檢視,也可以在 vue ui的介面中點選檢視):

 /* config.module.rule('svg') */
      {
        "test": /\.(svg)(\?.*)?$/,
        "use": [
          {
            "loader": "file-loader",
            "options": {
              "name": "img/[name].[hash:8].[ext]"
            }
          }
        ]
      },
複製程式碼

現在我們在vue.config.js檔案中,來改寫一下這個配置。

chainWebpack: config => {
// 我們先刪除原有的svg rule。
    config.module.rules.delete('svg')
    config.module
      .rule('svg-sprite-loader')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
    // 但我們有的svg可能並不都是圖示。我們的需要對icons/svg以外的svg檔案做處理。我們把images的rule新增上svg格式。
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/)
      
  }
複製程式碼

現在我們只要import進這個svg檔案就可以使用了。

 import "@/icons/sun.svg";
 
 // 使用
 <svg class="icon">
    <use xlink:href="#sun"></use>
  </svg>
複製程式碼

自動匯入webpack的require.context

上面示例中我們是手動import一個圖示,但一旦圖示多起來,手動操作就太不優雅了。我們可以使用webpack自帶的require.context功能來實現自動匯入功能。

// icons/index.js

//(建立出)一個 context,其中檔案來自 svg 目錄,不包含子目錄,request 以 `.svg` 結尾。
const req = require.context('./svg', false, /\.svg$/)

const requireAll = requireContext => requireContext.keys().map(requireContext)

requireAll(req)
複製程式碼

圖示再多,都可以毫無煩惱啦~~~

進一步壓縮 svgo-loader

其實我們引入的單個svg檔案中有很多無用的資訊。

svg圖示的webpack優化
svg-sprite-loader也做了相關處理,但是它只提取到path標籤整體,如果path路徑內包含的無用的註釋資訊,如:<path><!-- 無用資訊--></path>依舊存在。

我們可以使用 svgo-loader來進一步精簡svg的內容。刪除掉path 標籤內的註釋資訊。配置如下:

// vue.config.js
chainWebpack: config => {
    config.module.rules.delete('svg')
    config.module
      .rule('svg-sprite-loader')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
      .use('svgo-loader')
      .loader('svgo-loader')
      .end()
  }
複製程式碼

好了,這裡就是所有關於svg圖示使用和優化的全部內容了。如果有更好的方案和問題,歡迎大家指出和分享。

參考資料

  1. webpackg官網-管理依賴
  2. iconfont-使用說明
  3. 手摸手,帶你優雅的使用 icon

相關文章