几个月前我刚接触vue3时就研究过,一直搞忘了分享。
思路
就两点
- 首先要做一个vite plugin,作为一个loader加载
.svg
文件,读取svg文件的内容,类似raw-loader
。 - 然后需要一个component,它去动态加载svg文件,并把svg文件的内容拼接到雪碧图里。
代码
vite.config.ts
中这样写:
import {defineConfig, Plugin} from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from "fs";
import {dataToEsm} from "rollup-pluginutils";
const rawSvgPlugin:Plugin = {
name: 'raw-svg-file-loader',
transform(svg: string, filepath: string) {
// 判断后缀是否为svg
if (filepath.slice(-4) !== '.svg') return null;
const content = fs.readFileSync(filepath).toString()
return {
// 直接返回svg文件的原始内容
code: dataToEsm(content)
}
},
}
export default defineConfig({
plugins: [vue(), rawSvgPlugin],
})
IconSvg.vue
文件:
<script setup lang="ts">
// index.html里写一个<div id="SVG_CONTAINER"></div>
const SvgContainerId = "SVG_CONTAINER";
const props = defineProps({
name: { type: String, required: true }
});
const icon = ref("");
onMounted(() => {
watch(
() => props.name,
(name) => {
const id = `icon-${name}`;
const eId = "#" + id;
const container = document.getElementById(SvgContainerId);
// 存储已经加载过的svg
const svgIconList: Set<string> = (window as any).svgIconList || ((window as any).svgIconList = new Set());
if (!svgIconList.has(id)) {
svgIconList.add(id);
// 动态引入
import(`../assets/svg/${name}.svg`).then((res) => {
const svgElement = new DOMParser()
.parseFromString(res.default, "image/svg+xml")
.querySelector("svg");
if (svgElement) {
// 去掉无用的属性
for (const key of ["width", "height", "x", "y"]) {
svgElement.removeAttribute(key);
}
svgElement.id = id;
container!.appendChild(svgElement);
}
}).catch(() => svgIconList.delete(id));
}
icon.value = eId;
},
{ immediate: true }
);
});
</script>
<template>
<svg class="--icon-svg common-svg" aria-hidden="true">
<use :href="icon" />
</svg>
</template>
<style lang="scss">
svg.--icon-svg {
display: inline-flex;
align-items: center;
justify-content: center;
}
</style>
在main.ts
里只需要全局注册IconSvg组件就行了:
import { createApp } from 'vue'
import App from './App.vue'
import IconSvg from "./assets/svg/IconSvg.vue";
createApp(App).component('svg-icon', IconSvg).mount('#app')
这样使用:
<!-- 对应home.svg -->
<svg-icon name="home"/>
小结
这样做问题是解决了,可以动态导入svg并生成雪碧图,但是方式有点不优雅,有点投机取巧的感觉