优雅使用iconfont方案

588 阅读2分钟

前言

在项目开发中经常需要使用图标,有的直接使用UI切图,有的使用阿里图标库。阿里图标库里面的iconFont可以设置color、fontSize 是我们使用的主流。目前我在项目中使用svg格式来展示icon。

项目架构:umi3.5 + react hooks

为什么使用svg

  • SVG 可被非常多的工具读取和修改(比如记事本)
  • SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
  • SVG 是可伸缩的
  • SVG 图像可在任何的分辨率下被高质量地打印
  • SVG 可在图像质量不下降的情况下被放大
  • SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
  • SVG 可以与 Java 技术一起运行
  • SVG 是开放的标准
  • SVG 文件是纯粹的 XML

为什么不用font class了

  • 引入复杂,需要引入iconfont.css、iconfont.js、iconfont.woff等
  • 更新麻烦,每次版本迭代更新,多一样少一个都需要覆盖一整个文件,多人开发不友好(主要原因)
  • 上手需要成本、心智负担,尤其是较为青涩的成员独自修改风险较高
  • 代码难看,经常看到在项目入口文件一大串import

一、目录创建

新建一个专门存放svg的目录 image.png

二、umirc.ts 配置

yarn add svg-sprite-loader

svg-sprite-loader 的官方解释是:一个用于创建 svg 雪碧图的 Webpack 加载器。这个加载器现在已经被 JetBrains 公司收录和维护了。通俗的讲:svg-sprite-loader 会把你引入的 svg 塞到一个个 symbol 中,合成一个大的 svg,最后将这个大的 svg 放入 body 中。symbol 的 id 如果不特别指定,就是你的文件名。

import { defineConfig } from 'umi';
export default defineConfig({
   ...
  chainWebpack(config: any) {
    // 用svg-sprite-loader制作 svg-symbol,让我们可以直接使用 svg-use。
    config.module
      .rule('svg')
      .exclude.add(path.resolve(__dirname, './src/assets/svg')).end();  // 不包含的采用默认的 svg 规则 
    config.module.rule('image')
      .test(/\.svg$/i)
      .include.add(path.resolve(__dirname, './src/assets/svg')).end()
      .use('svg-sprite-loader')
      .loader(require.resolve('svg-sprite-loader'))
  }
});

配置成功后效果 image.png

三、封装SvgIcon组件

/*
 * @Descripttion: SvgIcon组件
 * @Author: JayShen
 * @Date: 2022-06-23 17:55:58
 * @LastEditors: JayShen
 * @LastEditTime: 2022-09-14 15:50:10
*/
import React from 'react';
import classNames from 'classnames';
import style from "./index.module.less"
try {
    // 利用 webpack 提供的 require.context API 来创建自己的 context module 动态引入 icon
    const importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
    importAll(require.context('@/assets/svg', true, /\.svg$/));
} catch (error) {
    console.error('importAll:', error);
}
type TCursor = 'auto' | 'pointer' | 'wait' | 'help' | 'move'
export type IconProps = {
    /** svg名称 */
    name: string;
    onClick?: () => void;
    color?: string;
    className?: string;
    size?: number;
    /** 鼠标形状 */
    cursor?: TCursor;
    style?: any
};

const SvgIcon: React.FC<IconProps> = (props) => {
    const { name, color, size = 20, onClick, className, cursor = 'auto' } = props
    return (
        <svg
            {
            ...props
            }
            onClick={onClick}
            className={classNames(style.svgIcon, className)}
            style={{ color, fontSize: size && `${size}px`, cursor: cursor, ...props.style }}
            aria-hidden="true"
        >
            <use xlinkHref={'#' + name} />
        </svg>
    );
};
export default SvgIcon;
// index.module.less
.svgIcon{
  width: 1em;
  height: 1em;
  fill: currentColor;
  overflow: hidden;
  display: inline-block;
}

四、使用

将组件引入后,在页面直接使用,name就是svg文件名,极其方便

<SvgIcon
  name="shanchu"
  size={16}
  color="#4389f1"
 />

解决痛点

  • 新增便捷,每次新增只需要新建svg文件,在阿里图标库点击【复制svg代码】即可
  • 心智负担小,团队只要一个人配置好,后续成员使用完全不需要修改配置、也不需要知道其原理
  • 使用简单,搭配组件后使用简单,只需要传入name
  • 代码优雅,不需要一堆import

扩展