本地svg与antd Icon封装为Icon组件

2,018 阅读2分钟

项目技术栈,vite, react

Icon组件的封装

项目打包完成上线后,有可能不连外网,因此要求所有的icon为本地文件,这里封装本地svg与antd两种图标

我们先看看最终的使用效果是怎么样的, 传入name,color,size


import Icon from '@/component/Atoms/Icon'

const Demo = () => {
 
  return (
    <div>
        <Icon name='airplay' color='#ff0000' size='100px' />
        <Icon.Ant name='DownCircleOutlined' color='#366213' size='100px' />
    </div>
  )
}

export default Demo

image.png

1,封装Icon

1.1 AntdIcon

封装antd icon首先要引入,4.X版本后,antd不允许使用<Icon type='IconName' />的写法,必须使用<IconName />, 我们当然可以手动引入所有的icon,然后进行封装,但是不够优雅,这边提供两种思路供参考。

// case1
import React from 'react'
import { IconProps } from '.'
import * as icon from '@ant-design/icons'


interface AntIconProps extends IconProps {}
const AntIcon: React.FC<AntIconProps> = (props) => {
  const { name, color ='#555', size = '1em' } = props

  const antIcon = (name: string) => {
    return React.createElement(icon[name], {
      style:{
        fontSize: size,
        color
      }
    })
  }

  return (
    <>
      {antIcon(name)}
    </>
  )
}

export default AntIcon

// case2
import React, { lazy } from 'react'
import { IconProps } from '.'


interface AntIconProps extends IconProps {}
const AntIcon: React.FC<AntIconProps> = (props) => {
  const { name, color ='#555', size = '1em' } = props

  const lazyLoadIcon = (name: string) => {
    return <React.Suspense fallback={<></>}>
      {React.createElement(lazy(() => import('@ant-design/icons').then(module => {
        return {default: module[name]};
      })), {
        style:{
          fontSize: size,
          color
        }
      })}
    </React.Suspense>
  }

  return (
    <>
      {lazyLoadIcon(name)}
    </>
  )
}

export default AntIcon

1.2 LoacalIcon

本地的svg在项目中封装成icon组件的样子使用,需要做以下几件事

安装vite-plugin-svg-icons插件,并在vite.config.ts文件中进行相关配置

// 
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import svgr from 'vite-plugin-svgr'
import viteCompression from 'vite-plugin-compression'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

plugins: [
    react(),
    svgr(),
    createSvgIconsPlugin({
      // Specify the icon folder to be cached
      iconDirs: [path.resolve(process.cwd(), 'src/yourSvgPath')],
      // Specify symbolId format
      symbolId: 'local-icon-[svgName]',

      // 删除svg文件stroke属性,使得icon颜色能够修改
      svgoOptions:{
        plugins: [
          {
            name: "removeAttrs",
            params: {attrs:['stroke']}
          },
        ]
      },

      /**
       * custom insert position
       * @default: body-last
       */
      inject: 'body-last',

      /**
       * custom dom id
       * @default: __svg__icons__dom__
       */
      customDomId: '__svg__icons__dom__',
    })
  ],

在main.js中引入插件

// 引入插件,用于显示本地svgIcon
import 'virtual:svg-icons-register'

封装组件LoacalIcon

import React from 'react';
import { IconProps } from '.';

interface LocalIconProps extends IconProps {}

const LocalIcon: React.FC<LocalIconProps> = (props) => {
  const {name, color = 'currentColor', size = '1em'} = props;
  const symbolId = `#local-icon-${name}`

  return (
    <svg stroke={color} width={size} height={size} aria-hidden="true">
      <use xlinkHref={symbolId} />
    </svg>
  )
}

export default LocalIcon;

2,统一导出

import React from 'react';
import AntIcon from './AntIcon';
import Local from './LoacalIcon';

export interface IconProps extends JtElement {
  name:string
  color?:string
  size?:string
}

interface IconSubComponents {
  Ant: React.FC<IconProps>;
}
/**
 * 图标组件接口
 * You can use it in following ways:
 * - <Icon name="xxx" color="xxx" size="xxx"  />
 * - <Icon.Ant name="xxx" color="xxx" size="xxx" />
 *
 * @param props IconProps
 * @returns React Component
 */
const Icon: React.FC<IconProps> & IconSubComponents = (props) => {
  const { ...rest } = props;
  return (
    <Local {...rest} />
  );
};

Icon.Ant = AntIcon;

export default Icon;

大功告成,这就封装了一个可以同时使用本地svgIcon与antdIcon的Icon组件