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

2022-09-07 14:34:34 +08:00
 SniperXu

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

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

vue3/vite/naive-ui/xions

2845 次点击
所在节点    Vue.js
10 条回复
daysv
2022-09-07 14:59:12 +08:00
啥动态不动态的, 统统放 public 当资源
Carseason
2022-09-07 15:04:03 +08:00
import("xxx")
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
2022-09-07 15:28:22 +08:00
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
2022-09-07 15:56:09 +08:00
打包的时候如果不知道要使用的 svg 那是不能打包进去的,所以解决办法要么就是打包的时候把所有的 svg 文件都打包进去,要么就单独找个 cdn 引用进来
thinkershare
2022-09-07 16:49:04 +08:00
你有 2 个选择, 第一个是再前端全部将 SVG 一次性全部导入, 然后定义好 SymbolId, 根据 name 转换到 SymbolId, 然后使用, 这种方式本身 SVG 图片是一次性导入到前端的, 只是显示什么 icon 是动态的. 另外一种就是根据 SVG 名称转换为完整的 cdn 提供的 https://xxx.svg 这种资源路径, 然后再 svg 标签中, href 直接引用.
dengqing
2022-09-07 18:36:19 +08:00
看看这个可以吗 https://github.com/Dunqing/vite-plugin-dynamic-import-module

之前有同样的需求动态加载 ant-design 的 icons
zhuweiyou
2022-09-07 22:16:37 +08:00
import.meta.globEager
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
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>

```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/878355

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX