源代碼 github.com/Maricaya/h5… 主要查看兩個文件
webpack.config.js
和/src/components/Icon.tsx
html
又到了愉快的總結時刻,此次咱們來看看怎麼優化項目中的 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 塞到一個個 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: {} }
]
}
複製代碼
<svg>
<use xlink:href="#xxx"/> // xxx 爲id
</svg>
複製代碼
設計小姐姐在切圖時,可能不會注意某些細節。
好比,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。
<svg className="icon" fill="#ccc">
<use xlinkHref={'#' + props.name} />
</svg>
複製代碼
最後,再單獨寫一個 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.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。
若是想引入一個文件夾下面的全部文件,或者引入能匹配一個正則表達式的全部文件,這個功能就會頗有幫助,例如:
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
一次引入全部文件。