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