源代码 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
一次引入全部文件。