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

vue3+vite 动态引入 SVG ICON 的问题

  •  
  •   SniperXu · 2022-09-07 14:34:34 +08:00 · 2729 次点击
    这是一个创建于 589 天前的主题,其中的信息可能已经有所发展或是发生改变。

    请教下各位大佬,我接口拿到后台返回 icon 的 name:string ,如何做动态引入呢?

    之前都是用的 iconfont ,不需要单独 import ,这回改用 SVG ICON 犯了难。

    vue3/vite/naive-ui/xions

    10 条回复    2022-09-26 19:25:28 +08:00
    daysv
        1
    daysv  
       2022-09-07 14:59:12 +08:00
    啥动态不动态的, 统统放 public 当资源
    Carseason
        2
    Carseason  
       2022-09-07 15:04:03 +08:00
    import("xxx")
    colatea
        3
    colatea  
       2022-09-07 15:11:29 +08:00
    main.js
    import * as ElementPlusIconsVue from '@element-plus/icons-vue'
    ...



    import { Search } from "@element-plus/icons-vue";
    ...
    components: { Search },
    ...
    <el-icon>
    <Search />
    </el-icon>
    haodaking
        4
    haodaking  
       2022-09-07 15:28:22 +08:00   ❤️ 1
    https://codesandbox.io/s/magical-carson-n6540i?file=/src/Icon.vue

    Icon.vue

    <script lang="ts">
    import { defineComponent, computed, defineAsyncComponent } from "vue";

    export default defineComponent({
    props: ["icon"],
    setup(props) {
    const iconCompoent = computed(() => {
    return defineAsyncComponent(async () => {
    return (await import("@vicons/ionicons5"))[props.icon];
    });
    });
    return {
    iconCompoent,
    };
    },
    });
    </script>

    <template>
    <n-icon :component="iconCompoent" />
    </template>


    使用


    Demo.vue

    <template>
    <Icon icon="GameController" />
    <Icon icon="GameController" size="40" color="#0e7a0d" />
    </template>

    <script lang="ts">
    import { defineComponent } from "vue";
    import Icon from "./Icon.vue";
    export default defineComponent({
    components: {
    Icon,
    },
    });
    </script>
    zcf0508
        5
    zcf0508  
       2022-09-07 15:56:09 +08:00
    打包的时候如果不知道要使用的 svg 那是不能打包进去的,所以解决办法要么就是打包的时候把所有的 svg 文件都打包进去,要么就单独找个 cdn 引用进来
    thinkershare
        6
    thinkershare  
       2022-09-07 16:49:04 +08:00   ❤️ 1
    你有 2 个选择, 第一个是再前端全部将 SVG 一次性全部导入, 然后定义好 SymbolId, 根据 name 转换到 SymbolId, 然后使用, 这种方式本身 SVG 图片是一次性导入到前端的, 只是显示什么 icon 是动态的. 另外一种就是根据 SVG 名称转换为完整的 cdn 提供的 https://xxx.svg 这种资源路径, 然后再 svg 标签中, href 直接引用.
    dengqing
        7
    dengqing  
       2022-09-07 18:36:19 +08:00 via iPhone
    看看这个可以吗 https://github.com/Dunqing/vite-plugin-dynamic-import-module

    之前有同样的需求动态加载 ant-design 的 icons
    zhuweiyou
        8
    zhuweiyou  
       2022-09-07 22:16:37 +08:00
    import.meta.globEager
    JayZXu
        9
    JayZXu  
       2022-09-08 08:23:17 +08:00
    目前用的这个包 vite-plugin-svg-icons
    https://github.com/vbenjs/vite-plugin-svg-icons/blob/main/README.zh_CN.md
    使用逻辑跟 webpack 的 svg 使用差不多,把 svg 放在文件夹里面就能生成对应的 icon name
    justin2018
        10
    justin2018  
       2022-09-26 19:25:28 +08:00
    ### Vue 中优雅使用 SVG

    #### 相关文章

    在 vue 项目中优雅的使用 Svg - 掘金
    https://juejin.cn/post/6844903697999200263

    在 vue3+vite 项目中使用 svg - 掘金
    https://juejin.cn/post/6932037172178616334

    通过 vite-plugin-svg-icons 插件封装 SvgIcon 组件 - 掘金
    https://juejin.cn/post/7094060278475653128

    ---

    #### 使用`vite-plugin-svg-icons`插件

    ##### 安装插件

    ```shell
    // 安装插件
    npm install svg-sprite-loader -D
    # via yarn
    yarn add svg-sprite-loader -D

    // 如果报错 需要安装“fast-glob”
    yarn add fast-glob -D
    ```

    ##### 封装 SvgIcon 组件

    ```javascript
    <template>
    <svg :class="svgClass" v-bind="$attrs" :style="{ color: color }">
    <use :xlink:href="iconName" />
    </svg>
    </template>

    <script>
    import { defineComponent, computed } from "vue";
    export default defineComponent({
    name: "SvgIcon",
    props: {
    name: {
    type: String,
    required: true,
    },
    color: {
    type: String,
    default: "#333",
    },
    },
    setup(props) {
    const iconName = computed(() => `#icon-${props.name}`);
    const svgClass = computed(() => {
    console.log(props.name, "props.name");
    if (props.name) {
    return `svg-icon icon-${props.name}`;
    }
    return "svg-icon";
    });

    return {
    iconName,
    svgClass,
    };
    },
    });
    </script>

    <style scoped>
    .svg-icon {
    width: 1em;
    height: 1em;
    fill: currentColor;
    vertical-align: middle;
    }
    </style>

    ```



    ##### 配置插件

    ```javascript
    // 配置 vite.config.js
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
    import { resolve } from 'path'

    // https://vitejs.dev/config/
    export default defineConfig({
    plugins: [
    vue(),
    createSvgIconsPlugin({
    iconDirs: [resolve(process.cwd(), 'src/assets/svg')],
    symbolId: 'icon-[dir]-[name]',
    })
    ],
    })

    // 配置 main.js
    import 'virtual:svg-icons-register'

    const app = createApp(App);

    app
    .component("svg-icon", SvgIcon)
    .mount('#app');
    ```

    ##### 使用插件

    ```vue
    <script setup>
    import HelloWorld from "./components/HelloWorld.vue";
    import SvgIcon from "./components/SvgIcon.vue";
    </script>

    <template>
    <div>
    <a href="https://vitejs.dev" target="_blank">
    <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
    <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
    </div>
    <HelloWorld msg="Vite + Vue" />
    <SvgIcon name="tree" />
    </template>

    <style scoped>
    .logo {
    height: 6em;
    padding: 1.5em;
    will-change: filter;
    }
    .logo:hover {
    filter: drop-shadow(0 0 2em #646cffaa);
    }
    .logo.vue:hover {
    filter: drop-shadow(0 0 2em #42b883aa);
    }
    </style>

    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3939 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 05:11 · PVG 13:11 · LAX 22:11 · JFK 01:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.