vue3 vite 使用 unplugin-icon 数据动态渲染图标

在使用 unplugin-icon 图标库的时候遇到的图标动态渲染问题,这里记录一下
正常直接使用<icon-carbon:user-avatar/> 这样的图标是正常渲染的,但是当遇到路由图标那种需要根据数据渲染出图标,就不行了,这里的解决方法是封装图标渲染的组件
绑定的时候就通过h函数进行渲染

建一个icon.ts文件

import { h } from 'vue';
import SvgIcon from '@/components/Icon/SvgIcon.vue';

/**
 * 图标渲染
 * - 用于vue的render函数
 */
export const useIconRender = () => {
  interface IconConfig {
    /**
     * 图标名称(iconify图标的名称)
     * - 例如:mdi-account 或者 mdi:account
     */
    icon?: string;
    /**
     * 本地svg图标文件名(assets/svg文件夹下)
     */
    localIcon?: string;
    /** 图标颜色 */
    color?: string;
    /** 图标大小 */
    fontSize?: number;
  }

  interface IconStyle {
    color?: string;
    fontSize?: string;
  }

  /**
   * 图标渲染
   * @param config
   * @property icon - 图标名称(iconify图标的名称), 例如:mdi-account 或者 mdi:account
   * @property localIcon - 本地svg图标文件名(assets/svg文件夹下)
   * @property color - 图标颜色
   * @property fontSize - 图标大小
   */
  const iconRender = (config: IconConfig) => {
    const { color, fontSize, icon, localIcon } = config;

    const style: IconStyle = {};

    if (color) {
      style.color = color;
    }
    if (fontSize) {
      style.fontSize = `${fontSize}px`;
    }

    if (!icon && !localIcon) {
      window.console.warn('没有传递图标名称,请确保给icon或localIcon传递有效值!');
    }

    return () => h(SvgIcon, { icon, localIcon, style });
  };

  return {
    iconRender
  };
};

然后创建个SvgIcon组件

<template>
  <template v-if="renderLocalIcon">
    <svg aria-hidden="true" width="1em" height="1em" v-bind="bindAttrs">
      <use :xlink:href="symbolId" fill="currentColor" />
    </svg>
  </template>
  <template v-else>
    <Icon v-if="icon" :icon="icon" v-bind="bindAttrs" />
  </template>
</template>

<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { Icon } from '@iconify/vue';

/**
 * 图标组件
 * - 支持iconify和本地svg图标
 * - 同时传递了icon和localIcon,localIcon会优先渲染
 */
interface Props {
  /** 图标名称 */
  icon?: string;
  /** 本地svg的文件名 */
  localIcon?: string;
}

const props = defineProps<Props>();

const attrs = useAttrs();

const bindAttrs = computed<{ class: string; style: string }>(() => ({
  class: (attrs.class as string) || '',
  style: (attrs.style as string) || ''
}));

const symbolId = computed(() => {
  const { VITE_ICON_LOCAL_PREFFIX: preffix } = import.meta.env;

  const defaultLocalIcon = 'no-icon';

  const icon = props.localIcon || defaultLocalIcon;

  return `#${preffix}-${icon}`;
});

/** 渲染本地icon */
const renderLocalIcon = computed(() => props.localIcon || !props.icon);
</script>

<style scoped></style>

vite的plugin这样引

import { resolve } from 'path'
import Icons from 'unplugin-icons/vite'
import VueMacros from 'unplugin-vue-macros/vite';
import IconsResolver from 'unplugin-icons/resolver';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { getSrcPath } from '../utils';

/**
 * * unplugin-icons插件,自动引入iconify图标
 * usage: https://github.com/antfu/unplugin-icons
 * 图标库: https://icones.js.org/
 */

export default function unplugin(viteEnv) {
	const { VITE_ICON_PREFFIX, VITE_ICON_LOCAL_PREFFIX } = viteEnv;

	const srcPath = getSrcPath();
	/** 本地svg图标路径 */
  const localIconPath = `${srcPath}/assets/svg`;

  /** 本地svg图标集合名称 */
  const collectionName = VITE_ICON_LOCAL_PREFFIX.replace(`${VITE_ICON_PREFFIX}-`, '');

	return [
		VueMacros({}),
		AutoImport({
			imports: ['vue', 'vue-router'],
			dts: false,
		}),
		Icons({
			compiler: 'vue3',
			customCollections: {
				[collectionName]: FileSystemIconLoader(localIconPath, svg =>
          svg.replace(/^<svg\s/, '<svg width="1em" height="1em" ')
        ),
			},
			scale: 1,
			defaultClass: 'inline-block'
		}),
		Components({
			types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
			resolvers: [
				IconsResolver({
					customCollections: [collectionName],
					componentPrefix: VITE_ICON_PREFFIX
				})
			],
			dts: 'src/typings/components.d.ts',
			// dts: false, // 不生成component.d.ts
		}),
		createSvgIconsPlugin({
      iconDirs: [localIconPath],
      symbolId: `${VITE_ICON_LOCAL_PREFFIX}-[dir]-[name]`,
      inject: 'body-last',
      customDomId: '__SVG_ICON_LOCAL__'
    })
	]
}

VITE_ICON_PREFFIXVITE_ICON_LOCAL_PREFFIX 其实就是个全局常量 代表图标的前缀而已
常量
常量这块 直接写死都一样的
code
使用的时候直接导出封装的useIconRender

import { useIconRender } from '@/utils';
const { iconRender } = useIconRender();
iconRender({ icon: 'carbon:user-avatar'}),

然后到渲染的地方呢 读取出你绑定的字段
code
<component :is="menu.meta.icon" /> 直接渲染就可以了
本地的图标好像有点问题,还在研究为啥动态渲染不了,后面发现问题了再改
或者有没有大佬看看,是啥原因导致本地svg动态导入不进去

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值