几个月前我刚接触 vue3 时就钻研过,始终搞忘了分享。
起因
我的 vue 我的项目个别都应用动静导入的 svg-sprite,意思是:既要是个雪碧图,用 <use> 标签复用;而且页面的 svg 应该是动静按需加载的。我感觉这样性能比拟好,在 vue2 中实现比较简单,然而 vue3+vite 下我并未找到适合的解决方案,只有雪碧图没有动静导入。于是乎我就想本人做一个。
思路
- 首先要做一个 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;
}
}
},
})
在 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 并生成雪碧图,然而形式有点不优雅,有点投机取巧的感觉
本文转载自我的集体博客:vite+vue3 下如何应用动静导入的 svg-sprite – 云与原的博客 (halberd.cn)