React:记账本08---优化:使用svg-sprite-loader、svgo-loader 封装svg-icon组件

1,221 阅读2分钟

需求分析

  • 项目引用了大量的svg-icon图表,正常react使用icon要这样:
<img src="/xxx/yyy/zzz/icon.svg" />
  • 现在想类似element一样,通过某个组件一行引入,类似:
<Icon name="icon-file-name"></Icon>
  • 过程中使用到2个loader: svg-sprite-loader、svgo-loader

先了解下sprite雪碧图

张鑫旭的博客

1. symbol

  • SVG Sprite最佳实践是使用symbol元素
  • 对于一个集合了三个SVG图标的SVG元素的代码结构会是这样:
<svg>
    <symbol>
        <!-- 第1个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第2个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第3个图标路径形状之类代码 -->
    </symbol>
</svg>
  • 每一个symbol就是一个图标元件

2. use

  • 只是放置了图标,如果你不使用(use),是看不见的
  • 这样使用
<use xlink:href="#symbolId"></use>

svg-sprite-loader

一个生成雪碧图的工具,它是一个 webpack loader ,可以将多个 svg 打包成 svg-sprite 。

原理

  • svg-sprite-loader会把你的icon塞到一个个symbol中,
  • symbol的id如果不特别指定,就是你的文件名。
  • 它最终会在你的html中嵌入一个大的svg,
<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>
  • 然后就可以像上面use一样使用了
<svg>
  <use xlink:href="#xxx"/> // xxx 为id
</svg>
  • 总结下就是:symbol + use => SVG Sprite

使用和配置

  1. 安装
yarn add --dev svg-sprite-loader
  1. 配置webpack
{
  test: /\.svg$/,
  loader: 'svg-sprite-loader',
}
  1. 使用 icon组件里
<svg>
  <use xlink:href="#svgName"/> 
</svg>

svgo-loader

介绍

SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.

  • 参考张鑫旭大佬的博客
  • github
  • 现在想改变svg-icon元素的样式和颜色等,可以使用svgo-loader
  • SVGO 是 svg 优化器,包含很多插件。
  • 它可以删除和修改SVG元素,折叠内容,移动属性等等等等。

原理

  • SVGO 将 SVG-as-XML 数据转换为 SVG-as-JS AST 表示形式。

  • 然后在所有AST数据项上运行并执行一些操作,最后,SVGO 再将 AST 转换回 SVG-as-XML 数据字符串。

使用和配置

  1. 安装
yarn add --dev svgo-loader
  1. webpack里配置
 {
      test: /\.svg$/,
      use: [
        {loader: 'svg-sprite-loader', options: {}},
        {
          loader: 'svgo-loader', options: {
            // plugins: [
            //   {removeAttrs: {attrs: 'fill'}}
            // ]
          }
        }
      ]
},
  • 注意:先svgo-loader,再svg-sprite-loader。先处理svg图像,在生成雪碧图
  1. 改颜色通过fill属性实现

基于以上,封装Icon组件

import React from "react";
 require('icons/money.svg')
 require('icons/tag.svg')
 require('icons/chart.svg')

type Props = {
    name:string
}
const Icon =(props:Props)=>{
    return (
        <svg fill='red' className="icon">
            <use xlinkHref={'#'+props.name}></use>
        </svg>
    )
};
export default Icon

1. 为什么使用require引入icon?

  • 避免tree-shaking
  • 使用import的话,会导致tree-shaking,而无法使用

2. 如果我引入的icon很多,那是不是要一个一个写呢?能不能一次性引入文件夹呢?

  • 可以
  • 代码
//require一个目录/文件夹
let importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
    requireContext.keys().forEach(requireContext);
try {
    importAll(require.context('icons', true, /\.svg$/));
} 
catch (error) 
    {console.log(error);
        
    }
  • 参考:webpack的require.context
  • require.context:
    • webpack提供的API,用来创建自己的context module,动态引入icon
    • 支持三个参数:
      • 要搜索的文件夹目录
      • 是否还应该搜索它的子目录,
      • 以及一个匹配文件的正则表达式。
    • 所以就是:搜索icons文件夹下,包含其中的子目录的所有svg格式的文件
  • 一个 context module会导出一个(require)函数,此函数可以接收一个参数:request。
    • 此导出函数有三个属性:resolve, keys, id。
    • resolve 是一个函数,它返回 request 被解析后得到的模块 id。
    • keys 也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请求(译者注:参考下面第二段代码中的 key)组成。
  • 如果想引入一个文件夹下面的所有文件,或者引入能匹配一个正则表达式的所有文件,这个功能就会很有帮助,例如:
function importAll(r) {
  r.keys().forEach(r);
}

importAll(require.context('../components/', true, /\.js$/));
  • 总的来说,就是说 require.context 帮我们创建一个上下文。

    • 比如在这里我们的上下文就是 ./src/assets/icons。

    • 随后我们就可以通过 require.resolve 来引入该上下文内的文件了。

3. 参考花裤衩大佬怎么封装的vue版本