antd 自定义 Icon 的几种方式及其优劣

@brickspert 2019-12-08 08:59:39发表于 brickspert/blog

虽然 antd 提供了大量的 Icon 图标,但是在平时的设计稿中,会出现各种设计师自定义的 Icon,对于这类图标,怎样处理更好呢?

多种方法

直接当做图片使用

svg 也是一种图片,可以作为 imgsrc 使用。

通过配置 url-loader 的配置,我们可以直接引用 svg 资源。

{
    test: /\.(png|jpg|gif|svg)$/,
    use: [{
        loader: 'url-loader',
        options: {
            limit: 8192
        }
    }]
}
import customSvg from '../assets/images/xxx.svg';

<img src={customSvg} />  

优劣

  • 快,无脑,无理解成本。
  • 不能像自带的 Icon 一样,设置 colorfont-size 等。

自定义 font 图标

参见官方文档

在 antd v3.9.0 之后,提供了一个 createFromIconfontCN 方法,方便开发者调用在 iconfont.cn 上自行管理的图标。

首先你需要在 iconfont 上创建自己的图标库,并上传自定义的 Icon,然后就可以通过自定义组件使用了。

iconfont 图标库

iconfont 上新建图标库比较简单,我就不赘述了。我讲一下在上传自定义 Icon 时的几个坑。

Q:自定义 SVG 上传后,显示为空白。

如果 SVG 图标不是封闭的,上传到 iconfont 之后,会显示为空白。那什么是封闭呢?大概就是图标有缺口~

比如下面的线条就是非封闭的,上传到 iconfont 就变成空白了。

1
2

我们只需要选中该图标,进行“轮廓化”处理即可。

3

Q:自定义 SVG 上传后,图标显示不全。

如果 SVG 图标由多个部分组成,但是没有进行“轮廓化”,那上传到 iconfont 之后,可能会显示不全。比如下图的图标,上传到 iconfont 后,图标显示不全了。

4
5

我们选中该图标多个部分,进行“轮廓化”即可。

6

**Q:自定义 SVG 上传后,无法通过 color 属性设置颜色。

我们从 sketch 导出的 svg,一般都是自带颜色的,我们在上传时,选择去除颜色并提交即可。

7

自定义 MyIcon 组件

经过上面的步骤,我们已经把需要的 Icon 上传到 iconfont 了,同时我们可以拿到字体的链接。

8

然后通过 antd 的 createFromIconfontCN 包装下即可使用。

const MyIcon = Icon.createFromIconfontCN({
  scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', // 在 iconfont.cn 上生成
});

/* 使用起来还比较简单 */
<MyIcon type="xxx"/>

优劣

  • 如果只管使用 MyIcon,而不管维护,那是比较爽的。
  • 维护很麻烦!每次上传一个新图标,字体链接都会变化一次,组件中就得替换一下,烦不胜烦啊。
  • 由于是通过字体包引用进来的,无法做到按需加载。

自定义 SVG 图标

通过 svgr 我们可以将一个普通的 svg 图片,转成 React 组件。

通过 webpack 将 SVG 转成组件

参见官方文档

// webpack.config.js
// umijs 配置见 https://github.com/umijs/umi/issues/1078
{
  test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  use: [
    {
      loader: 'babel-loader',
    },
    {
      loader: '@svgr/webpack',
      options: {
        babel: false,
        icon: true,
      },
    },
  ],
}
import { Icon } from 'antd';
import MessageSvg from 'path/to/message.svg'; // path to your '*.svg' file.

ReactDOM.render(<Icon component={MessageSvg} />, mountNode);

对于带颜色的图标,loader 并不知道 svg 上哪个颜色是需要自定义的,所以需要手动将 svg 中写死的 color 值改成 currentColor

9

优劣
  • 配置完 webpack 后,使用真的很简单。
  • 对于带颜色的 SVG 图标,需要手动去 svg 文件中改下 currentColor。

通过 cli 将 SVG 转成组件

svgr 可以通过 cli,将 SVG 转换为组件,同时可以将 svg 中的颜色,设置成变量。

正常情况下,我们导出的 SVG 长这样:

<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 58 (84663) - https://sketch.com -->
    <title>Group</title>
    <desc>Created with Sketch.</desc>
    <g id="组件" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="一级导航" transform="translate(-1197.000000, -18.000000)">
            <g id="Group" transform="translate(1197.000000, 18.000000)">
                <circle id="Oval" fill="#2F54EB" cx="10" cy="10" r="10"></circle>
                <g id="plus" transform="translate(3.000000, 3.000000)" fill-rule="nonzero">
                    <rect id="Rectangle-path" fill="#000000" opacity="0" x="0" y="0" width="14" height="14"></rect>
                    <path d="M11.59375,6.48046875 L7.51953125,6.48046875 L7.51953125,2.078125 L6.48046875,2.078125 L6.48046875,6.48046875 L2.40625,6.48046875 C2.34609375,6.48046875 2.296875,6.5296875 2.296875,6.58984375 L2.296875,7.41015625 C2.296875,7.4703125 2.34609375,7.51953125 2.40625,7.51953125 L6.48046875,7.51953125 L6.48046875,11.921875 L7.51953125,11.921875 L7.51953125,7.51953125 L11.59375,7.51953125 C11.6539063,7.51953125 11.703125,7.4703125 11.703125,7.41015625 L11.703125,6.58984375 C11.703125,6.5296875 11.6539063,6.48046875 11.59375,6.48046875 Z" id="Shape" fill="#FFFFFF"></path>
                </g>
            </g>
        </g>
    </g>
</svg>

通过 svgr 的命令行尝试转一下:

npx @svgr/cli --icon --replace-attr-values "#2F54EB=currentColor" message.svg

输出结果为:

import React from "react";

const MessageSvg = props => (
  <svg width="1em" height="1em" viewBox="0 0 20 20" {...props}>
    <g fill="none" fillRule="evenodd">
      <circle fill="currentColor" cx={10} cy={10} r={10} />
      <path
        d="M14.594 9.48H10.52V5.078H9.48V9.48H5.406a.11.11 0 00-.11.11v.82c0 .06.05.11.11.11H9.48v4.402h1.04V10.52h4.074c.06 0 .11-.05.11-.11v-.82a.11.11 0 00-.11-.11z"
        fill="#FFF"
        fillRule="nonzero"
      />
    </g>
  </svg>
);

export default MessageSvg;

good job~ 现在我们获得了一个纯 React 组件,直接使用即可。

import { Icon } from 'antd';
import MessageSvg from './MessageSvg';

ReactDOM.render(<Icon component={MessageSvg} />, mountNode);
优劣
  • 可以满足功能需要,获得一个可以自定义颜色的 Icon。
  • 项目中不是维护 svg 图片,而是维护转换过后的 React 组件。

总结

上面的几种方案我有在多个项目中尝试过,目前觉得 通过 webpack 将 SVG 转成组件 方案不错。

  • 首先可以满足需求,与 antd 原生 Icon 使用无差异,可以通过 colorfont-size 等属性控制样式。
  • 使用简单,配置完 webpack 之后,就变成傻瓜式操作了。
  • 可以按需加载。
  • 不依赖第三方,比如 iconfont。

❤️感谢大家

关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。

image