vue3按需加载动态icon
1、问题:
element-plus中icon跟element中的icon完全不同,使用也很多变化,最重要的一点就是按需动态加载了,主要用到unplugin-icons这个库
根据按需加载icon的使用方法,是无法满足动态加载的
<template> <IEpPlus /> </template>
因为比如侧边栏列表的icon等很多都需要动态加载,就会发现以下代码是无法按需加载icon的
<script setup > let obj = reactive([{ icon: 'IEpPlus' }, { icon: 'IEpMinus' }, { icon: 'IEpHouse' }, { icon: 'IEpDelete' }]) </script> <template> <div v-for="(item, i) in obj" :key="i"> <component :is="item.icon" /> </div> </template>
2、思路:
加载动态图标主要靠vue的component组件,而component导入大致分为两种,一个是注册为组件,然后传入组件名称字符串,第二个则是直接传入组件或者是html
所以其实解决问题方式就可以分成两类去解决:1、注册组件。2、传入组件
3、处理
注册解决法:网上很多的方案都是注册为vue组件,然后再使用。这样做是可以解决问题,但我不是很推荐,因为你会把没有用到的icon也注册进去,那也没必要使用按需加载了,而且自定义图标要另外的插件注册
// main.ts import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) }
组件传入法:配合unplugin-auto-import和unplugin-vue-components使用,它们让你不用手写import,极其方便
//vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import path from 'path' import Icons from 'unplugin-icons/vite' import { FileSystemIconLoader } from 'unplugin-icons/loaders' import IconsResolver from 'unplugin-icons/resolver' export default defineConfig({ css: { }, plugins: [ vue(), AutoImport({ imports: ['vue', 'vue-router'], resolvers: [ IconsResolver({ enabledCollections: ['my', 'ep'], }), ElementPlusResolver() ], }), Components({ resolvers: [ IconsResolver({ enabledCollections: ['my', 'ep'], }), ElementPlusResolver() ], }), Icons({ compiler: "vue3", autoInstall: true, customCollections: { 'my': FileSystemIconLoader('./src/assets/svg', svg => { svg = svg.replace(/fill="[A-Za-z0-9#(),]*"/g, '') svg = svg.replace(/width="[A-Za-z0-9]*"/g, '') svg = svg.replace(/height="[A-Za-z0-9]*"/g, '') svg = svg.replace(/^<svg /, '<svg fill="currentColor" ') return svg }) } }) ], resolve: { alias: { "~@": __dirname, "@": path.resolve(__dirname, "./src") } }, })
然后在组件中使用,不再是字符串的方式,而是直接引用
<script setup > let obj = reactive([{ icon: IEpPlus }, { icon: IEpMinus }, { icon: IEpHouse }, { icon: IEpDelete }]) </script> <template> <div v-for="(item, i) in obj" :key="i"> <component :is="item.icon" /> </div> </template>
在js中使用也一样,比如在路由中使用,要加个shallowRef,不然会报错,最后把这个导入component就行了
//router.js const routes = [ ... { ... meta: { icon:shallowRef(IEpPlus)
}
...
}
...
]
如果是通过接口获取icon,比如后端发送[{ icon: 'IEpPlus' } ,{ icon: 'IEpMinus' } ]过来,要怎么处理呢
那可以设置一个全局的对象来中转,比如下面pinia配置的例子:
//pinia设置全局对象 根据自己需求设置 export const useCounterStore = defineStore('counter', { state: () => ({ iconData:{ //可以是引入的图标比如element-plus的ep 'IEpPlus': IEpPlus, 'IEpMinus': IEpMinus,
... //也可以是自定义的图标,比如my 'IMyHome': IMyHome, 'IMyAdd': IMyAdd ... } }), actions: { getIcon(str) { return this.iconData[str] }, }, })
两种方法使用,一种是直接在v-for上使用,另一种是在获取数据时使用
<script setup > import axios from 'axios'; import { useCounterStore } from '@/store' const store = useCounterStore() let data1=[],data2=[] onMounted(()=>{ //获取[{ icon: 'IEpPlus' } ,{ icon: 'IEpMinus' } ,{ icon: 'IMyHome' } ,{ icon: 'IMyAdd' } ]数据 axios.get('/icon/xxx').then(res => { //处理 data1=res.map(e=>{ e.icon= store.getIcon(e.icon) return e }) //不处理 data2 = res }) }) </script> <template> <!-- 方式一 --> <div v-for="(item,k) in data1" :key="k"> <component :is="item.icon" /> </div> <!-- 方式二 --> <div v-for="(item, k) in data2" :key="k"> <component :is="store.getIcon(item.icon)" /> </div> </template>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)