V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qq309187341
V2EX  ›  Vue.js

求解 vite+vue3 项目中关于动态图标的问题

  •  
  •   qq309187341 · 2023-04-03 08:59:29 +08:00 · 2960 次点击
    这是一个创建于 634 天前的主题,其中的信息可能已经有所发展或是发生改变。
    项目使用了 vite+vue3 。然后配置了 unplugin-auto-import 、unplugin-vue-components 和 unplugin-icons 这三个插件。虽然实现了自动图标导入以及自动按需加载对应的图标功能。但是出现了一个问题,那就是菜单返回的动态图标没办法处理。
    然后目前遇上了一个问题,就是后端返回的图标名称是字符串的。但是我使用<component :is="xxx">需要一个组件。我想过用 eval()方法将后台的字符串转化成对象。但是这样会存在安全问题。
    有没有大佬教一下怎么处理。
    期望的需求是不需要显示的进行导入同时具备按需加载,自动导入的功能。
    21 条回复    2024-09-17 14:48:05 +08:00
    xiaohundun
        1
    xiaohundun  
       2023-04-03 09:15:52 +08:00
    yunyuyuan
        2
    yunyuyuan  
       2023-04-03 09:17:55 +08:00
    我是自己写的组件,不用第三方插件,动态加载没问题<svg-icon :name="your-variable" />
    https://blog.yunyuyuan.net/articles/9074
    qq309187341
        3
    qq309187341  
    OP
       2023-04-03 09:21:20 +08:00
    @yunyuyuan 你这个是项目内的 svg 图标吧。但是如果你需要使用第三方的图标呢?还是只能显示的导入再按需加载。
    yunyuyuan
        4
    yunyuyuan  
       2023-04-03 09:23:39 +08:00
    @qq309187341 1 楼已经提到了,unplugin-icons 不支持动态加载
    lupkcd
        5
    lupkcd  
       2023-04-03 09:25:06 +08:00
    使用 iconify 在线请求的方式,内部可搭私服
    qq309187341
        6
    qq309187341  
    OP
       2023-04-03 09:26:30 +08:00
    @xiaohundun 我通过 autoImport 插件实现了动态图标,但是现在存在一个问题,使用动态图标需要接受比如 let icon = IEpPlus 。 而后台返回的是 “IEpPlus”是一个字符串。我使用 eval()方法也成功实现了 “IEpPlus”转化成 IEpPlus 。但是 eval()有安全问题,我想问问还有什么别的方式,或者其他插件或者现有插件就有提供相关方式的
    clf
        7
    clf  
       2023-04-03 09:36:21 +08:00
    我看腾讯的 starter 脚手架里用的是 vite-svg-loader ,加载的 svg 是一个组件。

    import MyIcon from 'xxx/xxx.svg'

    上面这样引入 svg 文件就行。
    Huelse
        8
    Huelse  
       2023-04-03 09:47:42 +08:00
    上图床更可控点
    kingterrors
        9
    kingterrors  
       2023-04-03 09:52:35 +08:00   ❤️ 3
    我说前几天好像看过一个类似的帖子。。。果然是你发的。。。请不要频繁面向 V2EX 编程。
    而且上一个帖子我就说了,写个 demo ,有兴趣研究的人,直接在 demo 上手。方便你我他,然而你忽略了我的建议。
    任何新手在提问前,都应该尽可能将问题描述清楚,并方便解决人来尽快解决。
    甚至你在自己写个 demo 的时候,问题说不定就解决了。
    而打那么多字,你也费劲,别人理解也费劲。
    请下次提问带上 demo 。
    关联: https://www.v2ex.com/t/928689#r_12888230
    fox2081
        10
    fox2081  
       2023-04-03 09:56:12 +08:00   ❤️ 1
    推荐 unocss 的图标处理方案,直接加 class 名,无需额外的组件,自定义的图标可以配置好自动导入使用,动态图标直接写在配置中的 safelist 或者只要页面上有对应的类名能触发到就行
    han3sui
        11
    han3sui  
       2023-04-03 10:12:31 +08:00
    试试 new URL(url, [base])
    renmu
        12
    renmu  
       2023-04-03 10:13:30 +08:00 via Android
    搞不懂,你们后端返回图片是不返回链接的吗?
    qq309187341
        13
    qq309187341  
    OP
       2023-04-03 10:18:12 +08:00
    @renmu 因为这个是图标,而且是后台管理系统的菜单图标。所以可能就是这个图标的字符串
    xiaohundun
        14
    xiaohundun  
       2023-04-03 10:45:03 +08:00
    我觉得应该用 10 楼的方案,用 class 来做,要是你能处理 eval 的安全问题,那现在的也行
    xiaohundun
        15
    xiaohundun  
       2023-04-03 10:45:45 +08:00
    @xiaohundun #14 至少我见过的管理端的图标都是 classname
    shakukansp
        16
    shakukansp  
       2023-04-03 12:20:47 +08:00
    写个脚本
    比如你图标放 /assets/icons
    每次新加图标就运行一下脚本
    这个脚本就做一件事情,读取这个目录的文件名然后生成一个 ts 文件
    import BasicActtive from '~icons/assets-icons/basic-acttive';
    export {
    BasicActtive,
    }
    类似这种各式
    我项目里就是这么处理
    后端返回的是 icon 字符串
    前端导入
    import Bookmark from '~icons/carbon/bookmark';
    import * as icons from '~/assets/icons';

    export const getIcon = (name?: string) => {
    if (!name) return Bookmark;
    return Reflect.get(
    icons,
    camelcase(name, { pascalCase: true, preserveConsecutiveUppercase: true }),
    );
    };
    shakukansp
        17
    shakukansp  
       2023-04-03 12:24:18 +08:00
    ~icons/assets-icons/是 unplugin-icons 配置的自定义目录
    用 vite-svg-loader 也可以
    gitignore
        18
    gitignore  
       2023-04-03 14:20:40 +08:00
    @qq309187341 #6

    <component :is="vue: IEpPlus" /> vue 自动会将 `vue:` 前缀字符串解析到对应的组件,op 是这个意思吗?

    https://cn.vuejs.org/api/built-in-special-attributes.html#is

    vue 帮你调用 `resolveComponent`
    LucasW
        19
    LucasW  
       2023-04-03 14:32:13 +08:00
    duanluan
        20
    duanluan  
       104 天前
    src/components/Iconify.vue:

    <template>
    <el-icon>
    <Icon :icon="icon"/>
    </el-icon>
    </template>

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

    const props = defineProps({
    icon: {
    type: String,
    default: null,
    }
    });
    </script>

    src/components/RecursiveMenu.vue:

    <template>
    <template v-for="item in menuTree">
    <template v-if="item.children && item.children.length > 0">
    <el-sub-menu :index="String(item.id)">
    <template #title>
    <Iconify :icon="item.icon"/>
    <span>{{ item.name }}</span>
    </template>
    <RecursiveMenu :menu-tree="item.children"/>
    </el-sub-menu>
    </template>
    <template v-else>
    <el-menu-item :index="String(item.id)">
    <Iconify :icon="item.icon"/>
    <span>{{ item.name }}</span>
    </el-menu-item>
    </template>
    </template>
    </template>

    <script setup lang="ts">
    import Iconify from "@/components/Iconify.vue";

    const props = defineProps({
    menuTree: {
    type: Array as PropType<any[]>,
    required: true
    }
    });
    </script>
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5366 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 05:57 · PVG 13:57 · LAX 21:57 · JFK 00:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.