乐趣区

关于前端:vitevue3下如何使用动态导入的svgsprite雪碧图

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

退出移动版