几个月前我刚接触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
文件:
<template>
<svg aria-hidden="true">
<use :href="getName"></use>
</svg>
</template>
<script lang="ts">
import {defineComponent} from "vue";
const svgParser = new DOMParser();
export default defineComponent({
name: "IconSvg",
props: {
name: {
type: String,
default: ''
}
},
data (){
return {
getName: ''
}
},
watch: {
// 监听 name 变化
'$props.name': {
// 首次执行
immediate: true,
async handler (){
// 拼接svg文件名
const getId = `icon-${this.name}`
const name = `#${getId}`
// 动态加载
const res = await import(`../svg/${this.name}.svg`);
// 雪碧图的DOM容器
let container = document.querySelector('#_SVG_SPRITE_CONTAINER_');
if (!container || !container.querySelector(name)) {
if (!container) {
// 如果还未创建容器,就创建一个。(此处也可以直接写在index.html里)
container = document.createElement('div');
container.id = '_SVG_SPRITE_CONTAINER_'
container.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
container.setAttribute('style', 'position: absolute; width: 0; height: 0;overflow: hidden')
document.body.insertBefore(container, document.body.children[0]);
}
if (!container.querySelector(name)) {
// 如果容器内没有该svg,则解析并制作该svg的雪碧图
const svgElement = svgParser.parseFromString(res.default, "image/svg+xml").querySelector('svg');
if (svgElement) {
//删除影响样式的属性
for (const key of ['width', 'height', 'x', 'y']) {
svgElement.removeAttribute(key)
}
svgElement.id = getId
// 插入到容器里
container.appendChild(svgElement as SVGSVGElement)
}
}
}
this.getName = name;
}
}
},
})
</script>
在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并生成雪碧图,但是方式有点不优雅,有点投机取巧的感觉