使用 svg-sprite-loader、svgo-loader 優化項目中的 Icon

源代碼 github.com/Maricaya/h5… 主要查看兩個文件 webpack.config.js/src/components/Icon.tsxhtml

背景

又到了愉快的總結時刻,此次咱們來看看怎麼優化項目中的 Icon。react

每次寫項目引入 icon 的時候都寫一大堆:webpack

<img src="icons/chart.svg" alt="" />
複製代碼

最好能在項目中直接實現 element-ui 的引入效果:git

<Icon name="icon-file-name"/>
複製代碼

直接一句話就能夠引入 Icon,這樣多方便呀。github

網上搜尋了一圈,找到了兩個很是好用的 loader:web

svg-sprite-loader、svgo-loader正則表達式

來看看他們是怎麼工做的吧!typescript

svg-sprite-loader

原理

svg-sprite-loader 會把你的 svg 塞到一個個 symbol 中,合成一個大的 svg。element-ui

最後將這個大的 svg 放入 body 中。數組

symbol的id若是不特別指定,就是你的文件名。

在頁面上造成這樣的元素:

<body>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="__SVG_SPRITE_NODE__">
      <symbol xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024" id="xxx">// id 是 icon 名
	<!-- 這塊是 path -->
      </symbol>
      <symbol xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024" id="xxx">// id 是 icon 名
	<!-- 這塊是 path -->
      </symbol>
    </svg>
</body>
複製代碼

咱們的每個 icon 都對應着一個 symbol 元素,這個時候咱們就能夠在頁面使用 svg use 啦。

<svg>
  <use xlink:href="#xxx"/> // xxx 爲id
</svg>
複製代碼

咱們能夠把 symbol 理解爲 sketch 中內置的圖形。

當你須要使用這個圖形的時候,把這個形狀」拉」到你的畫板中就好了。

而 use 就是這個」拉」的行爲。

總結一下工做原理:

利用 svg 的 symbol 元素,將每一個 icon 包裹在 symbol 中,經過 use 使用該 symbol。

使用方法

安裝

yarn add --dev svg-sprite-loader
複製代碼

配置 webpack.config.js,在 module.rules 添加:

{
  test: /\.svg$/,
  use: [
    { loader: 'svg-sprite-loader', options: {} }
  ]
}
複製代碼

icon 寫法

<svg>
  <use xlink:href="#xxx"/> // xxx 爲id
</svg>
複製代碼

SVGO

背景

設計小姐姐在切圖時,可能不會注意某些細節。

好比,PSD 看上去是好的,可是放大個 100 倍,路徑的轉角和邊緣都沒對上。

此時若是直接使用 SVG,一個是 SVG 文件太大,二是最終的圖像可能不是咱們想要的。

這個時候就須要 SVGO 來幫咱們處理 svg。

原理

SVGO 將 SVG-as-XML 數據轉換爲 SVG-as-JS AST 表示形式。

而後在全部AST數據項上運行並執行一些操做,最後,SVGO 再將 AST 轉換回 SVG-as-XML 數據字符串。

想深刻了解原理的同窗,能夠看看 官方解釋

我就不在這裏囉嗦啦。

用法

SVGO 是 svg 優化器,包含不少插件。

它能夠刪除和修改SVG元素,摺疊內容,移動屬性等等等等。

安裝

yarn add --dev svgo-loader
複製代碼

繼續配置 webpack.config.js

{
  test: /\.svg$/,
  use: [
    { loader: 'svg-sprite-loader', options: {} },
+   { loader: 'svgo-loader', options: {} }
  ]
}
複製代碼

引入項目中的 svg 文件會通過 svgo-loader => svg-sprite-loader 的處理。

先處理 svg 圖像,而後在頁面中生成 svg-symbols。

icon 寫法

<svg className="icon" fill="#ccc">
  <use xlinkHref={'#' + props.name} />
</svg>
複製代碼

Icon 組件化

最後,再單獨寫一個 Icon 組件。

完整代碼以下:

Icon.tsx

import React from 'react'
// TreeShaking 不適用於 require
require('icons/money.svg')
require('icons/tag.svg')
require('icons/chart.svg')

type Props = {
  name: String
}

const Icon = (props: Props) => {
  return (
    <svg className="icon"> <use xlinkHref={'#' + props.name} /> </svg> ) } export default Icon; 複製代碼

使用時:

<Icon name="money" />
複製代碼

一次性引入全部 Icon

作完了 Icon 組件,又發現一個小小的問題。

在 Icon.tsx 文件中引入 svg 須要一個一個引入,能不能一次性所有引入呢?

能夠,不想一直重複引入,咱們須要 require 一個目錄。

// 不想一直重複引入,須要 require 一個目錄
// 由於使用了 TypeScript 須要安裝 webpack 的類型文件 @types/webpack-env
// yarn add --dev @types/webpack-env
let importAll = (requireContext: __WebpackModuleApi.RequireContext) => 
	requireContext.keys().forEach(requireContext);

try {importAll(require.context('icons', true, /\.svg$/));} 
	catch (error) {console.log(error);} 
複製代碼

利用 webpack 提供的 require.context API 來建立本身的 context module 動態引入 icon。

require.context(directory, useSubdirectories = false, regExp = /^\.\//)
複製代碼

它接受三個參數

  • 要搜索的文件夾目錄
  • 是否還應該搜索它的子目錄,
  • 以及一個匹配文件的正則表達式。

對於咱們的項目來講,咱們須要動態引入的就是

require.context('icons目錄', true, /\.svg$/)
複製代碼

require.context 會返回一個函數,而且該函數有keys(),id, resolve() 屬性。

一個 context module 會導出一個(require)函數,此函數能夠接收一個參數:request。

此導出函數有三個屬性:resolve, keys, id。

  • resolve 是一個函數,返回的是請求的 module 的 id
  • keys 也是一個函數,它返回一個數組,是知足該參數的模塊。
  • id是該 context module 的id

若是想引入一個文件夾下面的全部文件,或者引入能匹配一個正則表達式的全部文件,這個功能就會頗有幫助,例如:

function importAll (r) {
  r.keys().forEach(r);
}

importAll(require.context('../components/', true, /\.js$/));
複製代碼

總的來講,就是說 require.context 幫咱們建立一個上下文。

好比在這裏咱們的上下文就是 ./src/assets/icons

隨後咱們就能夠經過 require.resolve 來引入該上下文內的文件了。

總結

最後,來總結一下 Icon 組件的優化方式。

  • 使用svg-sprite-loader製做 svg-symbol。讓咱們能夠直接使用 svg-use。
  • 使用 svgo-loader 優化 svg。
  • 最後,使用 require.context 一次引入全部文件。
相關文章
相關標籤/搜索