几个月前我刚接触 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)