分享一个在React项目和Vue项目通用的Icon图标使用方法,扩展性强

647 阅读5分钟

前言

之前我在项目中使用Icon图标习惯使用iconfont的形式。但是渐渐觉得,这种方式需要固定下载并引入字体的ttf文件和woff文件。并且使用iconfont的形式,自己一个人写项目的时候是爽了,但是一旦项目交给其他人了,别人想继续添加新的icon图标,是没办法在你之前的icon图标文件上添加新图标的。必须得重新下载引入ttf文件和woff文件,定义新的字体和icon类名。而且使用iconfont的形式的icon元素语义不直观,别人看到这行代码很难分辨出这是个什么图标。

所以我选择直接使用icon图标的svg代码加组件封装的形式,具体如下:

React中使用

1. 先安装 @ant-design/icons

npm install @ant-design/icons

2. 新建一个CreateIcon.tsx文件(注意:文件是tsx文件)

import type { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon";
import Icon from "@ant-design/icons";
type OtherProps = {
  /**图标的大小,会转换为style-fontSize写入组件 */
  size?: number;
  /**图标的颜色,会转换为style-color写入组件 */
  color?: string;
};

const mergeObj = <T extends Obj, D extends Obj[]>(mainObj: T, ...otherObj: D): T & UnionToIntersection<D[number]> => {
  return Object.assign({}, mainObj, ...otherObj)
}

/**创建一个icon组件,传入svg代码
 * @param Svg 符合条件的svg代码
 * @tip 条件
 * 基于SVG,可以在 https://www.iconfont.cn/ 上寻找喜欢的图标,然后复制svg代码
 * 1. 把复制下来的代码中svg的属性,t和class删掉,就可以没有ts报错,然后宽高改为1em
 * 2. 如果需要颜色跟随自己定制的颜色,需要给svg的path标签的fill属性设置为"currentColor"
 * @returns
 */
export const createIcon = (Svg: JSX.Element) => {
  return function _Icon(props: Partial<CustomIconComponentProps> & OtherProps) {
    const { size, color, style = {}, ...IconProps } = props;
    const realStyle = mergeObj({ fontSize: size, color }, style);
    return <Icon component={() => Svg} {...IconProps} style={realStyle} />;
  };
};
export default createIcon;

再新建一个MyIcon.tsx文件

import { createIcon } from ".";
export const Audio = createIcon(
  <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5118" width="1em" height="1em"><path className="audioSvg" d="M566.215111 899.811556v118.158222h-85.333333v-114.915556C294.4 888.718222 147.342222 735.573333 147.342222 562.688a42.666667 42.666667 0 0 1 85.333334 0c0 134.257778 123.790222 256.113778 276.764444 256.113778s276.707556-121.912889 276.707556-256.113778a42.666667 42.666667 0 1 1 85.333333 0c0 164.067556-132.380444 310.385778-305.265778 337.123556zM510.976 33.336889a170.666667 170.666667 0 0 1 170.666667 170.666667v341.333333a170.666667 170.666667 0 1 1-341.333334 0v-341.333333a170.666667 170.666667 0 0 1 170.666667-170.666667z" fill="#1296DB" p-id="5119"></path></svg>
)
  1. 复制自己喜欢的icon图标的svg代码,粘贴到createIcon函数的参数里。
  2. 把复制下来的代码中svg的属性,t和class删掉,就可以没有ts报错,然后宽高改为1em
  3. 如果需要颜色跟随自己传入的颜色,需要给svg的path标签的fill属性设置为"currentColor"
  4. 最后导出的Audio就是Icon组件,可以直接使用

后面想要多少个icon就按照这种方法添加就行

最后在组件中使用

import { Audio } from "@/components/base/UI/MyIcon.tsx"
export default function FunctionComponent(){
    return (
        <div>
            {/* 注意这里如果要传入color参数的话,记得把图标svg代码的fill属性设置为"currentColor",这样color参数才会生效 */}
            <Audio size={30} />
        </div>
    )
}

在Vue项目中使用

首先要配置一下vue配置,使其支持jsx语法

配置Vue

  1. 安装 @vitejs/plugin-vue-jsx 和 element-plus
npm install @vitejs/plugin-vue-jsx element-plus
  1. 配置 vite.config.ts

import VueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
      plugins: [
        .....
        vueJsx({
          transformOn: true,
          mergeProps: true
        })
      ],
})

配置tsconfig.json

{
       ......
      "compilerOptions": {
        .........
        "jsx": "preserve",
        "jsxImportSource": "vue"
      }
}

新建一个CreateIcon.tsx文件 (注意是tsx文件)

import { defineComponent } from 'vue'
import type { JSX } from 'vue/jsx-runtime';
import { ElIcon } from "element-plus"

type OtherProps = {
    /**图标的大小 */
    size?: number;
    /**图标的颜色 */
    color?: string;
};
/**创建一个icon组件,传入svg组件
 * @param Svg 符合条件的svg组件
 * @tip 条件
 * 基于SVG,可以在 https://www.iconfont.cn/ 上寻找喜欢的图标,然后复制svg代码
 * 1. 把复制下来的代码中svg的属性,t和class删掉,就可以没有ts报错,然后宽高改为1em
 * 2. 如果需要颜色跟随自己定制的颜色,需要给svg的path标签的fill属性设置为"currentColor"
 * @returns
 */
export const createIcon = (Svg: () => JSX.Element) => {
    return defineComponent<OtherProps>(
        (props, { emit, slots, attrs }) => {
            const { size, color } = props;
            return () => (
                <ElIcon size={size} color={color}>
                    <Svg></Svg>
                </ElIcon>
            )
        }
    )
};

export default createIcon

新建一个MyIcon.tsx文件 (注意是tsx文件)

import { createIcon } from ".";
export const XiaoXiIcon = createIcon(
    () => (
        <svg
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="951"
            width="1em"
            height="1em"
        >
            <path
                d="M513.4 97.2c-220.5 0-399.2 156.4-399.2 349.3 0 110.3 58.5 208.4 149.7 272.5v176.7l174.9-106.1c24.2 4 49.1 6.3 74.6 6.3 220.5 0 399.2-156.4 399.2-349.3 0.1-193-178.7-349.4-399.2-349.4z m124.8 474.1H388.7c-13.8 0-25-11.2-25-25s11.2-25 25-25h249.5c13.8 0 25 11.2 25 25s-11.2 25-25 25z m49.9-149.8H338.8c-13.8 0-25-11.2-25-25s11.2-25 25-25h349.3c13.8 0 25 11.2 25 25 0 13.9-11.2 25-25 25z"
                fill="currentColor"
                p-id="952"
            ></path>
        </svg>
    )
)
  1. 复制自己喜欢的icon图标的svg代码,粘贴到createIcon函数的参数里。(注意:这里粘贴svg代码时,要写成函数返回值的形式,如上 () => (<svg>.......<\svg>)
  2. 把复制下来的代码中svg的属性,t和class删掉,就可以没有ts报错,然后宽高改为1em
  3. 如果需要颜色跟随自己传入的颜色,需要给svg的path标签的fill属性设置为"currentColor"
  4. 最后导出的XiaoXiIcon就是Icon组件,可以直接使用

后面想要多少个icon就按照这种方法添加就行

在Vue组件中使用

<template>
    <div>
        <!--注意这里如果要传入color参数的话,记得把图标svg代码的fill属性设置为"currentColor",这样color参数才会生效 -->
        <XiaoXiIcon :size="30" color="#1296db" />
    </div>
</template>
<script setup lang="ts">
import { XiaoXiIcon } from "@/components/icons/createIcon";
</script>

其他

当然如果不想在vue中使用jsx语法,也可以使用vue提供的h函数来生成虚拟dom,也可以做到像这样传入svg代码生成icon组件的效果,也可以直接创建一个.vue文件做为icon组件,这里展示一下

新建icon图标的.vue组件

//IconBack.vue
<template>
  <svg
    t="1725352194401"
    class="icon"
    viewBox="0 0 1024 1024"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    p-id="2464"
    :width="props.size"
    :height="props.size"
  >
    <path
      d="M682.424996 911.667585c-8.490372 0-17.041118-2.841721-24.091698-8.685823L199.142038 522.585079c-8.662287-7.185656-13.682627-17.853623-13.682627-29.112038s5.02034-21.926382 13.682627-29.111015L658.333298 83.941807c16.068977-13.325493 39.902802-11.06194 53.228295 4.995781 13.313214 16.081257 11.073196 39.901779-4.995781 53.228295L282.502607 493.473041 706.565812 844.756663c16.068977 13.326517 18.308995 37.147038 4.995781 53.228295C704.080202 907.004379 693.301717 911.667585 682.424996 911.667585z"
      p-id="2465" :fill="props.color || ''"
    ></path>
  </svg>
</template>

<script setup lang="ts">
interface iconProps {
  /**图标的大小 */
  size?: string;
  /**图标的颜色 */
  color?: string;
}

const props = defineProps<iconProps>();
</script>

每新增一个icon就新建一个.vue组件

image.png

最后在组件中直接引入并使用icon组件即可

最终效果

image.png

image.png