如何在 React 项目中优雅的使用 SVG

13,793 阅读1分钟

SVG 是一种基于 XML 语法的图像格式的矢量图。且不管放大多少倍都不会失真。由于项目中需要用到图标。然而在 React 版记账项目中应用 SVG 的过程并不是很顺利... anyway 最终功能实现啦~

当作图片使用

  • 导入:
import x from 'label.svg'
  • 使用
<img src={x} alt=""/>

弊端

  • 我们无法实现设置图片的颜色等其它功能
  • 重复的 SVG 代码就要复制粘贴,后期维护工作量巨大。

因此,为何不能组件化呢?其实使用 SVG 配合 use 标签就实现该功能

当作 SVG Symbol 使用

由于需要对应的 loader 但是 React 应用没有 webPack 配置文件,因此需要自定义 webpack

自定义 webpack

  1. 运行 yarn eject 拿到 webpack 配置
  2. 安装 svg-sprite-loader:yarn add --dev svg-sprite-loader
  3. 安装 svgo-loader:yarn add --dev svgo-loader
  4. 在 config/webpack.config.js 文件中 配置 两个 loader
{
     test: /\.svg$/,
      use: [
          	{loader: 'svg-sprite-loader', options: {}},
          	{loader: 'svgo-loader', 
                 options: {plugins:[{removeAttrs: {attrs: 'fill'}}]}
             }
            ]
 },

安装完可能会报错,问题不大根据提示操作即可。

使用

svg 里面要包含 use 标签, id 对应 # 加上 svg 的文件名

<svg><use xlinkHref=id/></svg>

举个栗子:

// 导入
import x from 'label.svg'
// 使用
<svg><use xlinkHref='#label'/></svg>

图片未显示??? 触发了 TreeShaking

TreeShaking

默认删除没有用到的变量

  • 可以看到没有明显的使用 x,只是把 svg 导入进来了
  • 因此会被 TreeShanking
  • 防止引入的 SVG 被 TreeShaking,使用 require 代替 import
 require('label.svg')
 <svg><use xlinkHref='#label'/></svg>

每次使用 SVG 要以上操作 ,为什么不将使用 SVG 的代码封装成组件使用呢?

创建 Icon 组件(组件化重点!)

  • 一次性引入所有的 SVG

  • 在 icons 目录通过文件名当作 id引用到任何组件

Icon.tsx

import React from 'react';

let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
try {importAll(require.context('icons', true, /\.svg$/));} 
catch (error) {console.log(error);}

type Props = {
  name: string
} & React.SVGAttributes<SVGElement>

const Icon = (props: Props) => {
  return (
    <svg>
     <use xlinkHref={'#' + props.name}/>
    </svg>
  );
};

export default Icon;

当需要用到 SVG ,直接引用 Icon 组件同时向组件传入 SVG 文件名是不是相当方便呢

 <Icon name="left"/>

一次性引入所有 SVG 的代码报错

安装 :yarn add --dev @types/webpack-env@1.15.1

loader 到底做了什么?

svg-sprite-loader

  • svg-sprite-loader 发现有 svg 标签,就把 svg 变成 symbol,再在 symbol 外面套一层 svg 标签,最后把 svg 放到 body 里面。
  • 为什么要套一层svg 呢?因为可能会有很多 SVG
  • 通过 use 标签即可使用 SVG

svgo-loader

当 SVG 默认使用了 fills 属性设置颜色,导致无法换色

  • 配置 svgo-loader 删除 SVG 自带的fill 属性,从而可通过设置 fill自定义 SVG 颜色的