I studied vuE3 a few months ago when I was first introduced to it and kept forgetting to share it.

The cause of

My vUE projects generally use dynamically imported SVG-Sprite, which means either a Sprite image, reused with the

tag; And the SVG of the page should be dynamically loaded on demand. I think the performance is better in this way, and it is easier to implement in VUe2, but I did not find a suitable solution under VUe3 + Vite, only Sprite images are not dynamically imported. So I wanted to make one myself.

Train of thought

  • Let’s do one firstvite plugin, as a loader.svgFile to read the contents of an SVG file, similarraw-loader.
  • Then you need a component that dynamically loads the SVG file and splices the content of the SVG file into a Sprite image.

code

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) {
    // Determine whether the suffix is SVG
    if (filepath.slice(-4)! = ='.svg') return null;
    const content = fs.readFileSync(filepath).toString()
    return {
      // Returns the original content of the SVG file directly
      code: dataToEsm(content)
    }
  },
}
export default defineConfig({
  plugins: [vue(), rawSvgPlugin],
})
Copy the code

IconSvg. Vue file:

<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: {
    // Listen for name changes
    '$props.name': {
      // First execution
      immediate: true.async handler (){
        // Concatenate SVG file names
        const getId = `icon-The ${this.name}`
        const name = ` #${getId}`
        // Dynamic loading
        const res = await import(`.. /svg/The ${this.name}.svg`);
        // Sprite map DOM container
        let container = document.querySelector('#_SVG_SPRITE_CONTAINER_');
        if(! container || ! container.querySelector(name)) {if(! container) {// Create a container if you haven't already. (You can also write it directly in 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)) {// If the SVG does not exist in the container, parse and make a Sprite diagram of the SVG
            const svgElement = svgParser.parseFromString(res.default, "image/svg+xml").querySelector('svg');
            if (svgElement) {
              // Remove attributes that affect styles
              for (const key of ['width'.'height'.'x'.'y']) {
                svgElement.removeAttribute(key)
              }
              svgElement.id = getId
              // Insert into the container
              container.appendChild(svgElement as SVGSVGElement)
            }
          }
        }
        this.getName = name; }}}})</script>
Copy the code

In main.ts you only need to register the IconSvg component globally:

import { createApp } from 'vue'
import App from './App.vue'
import IconSvg from "./assets/svg/IconSvg.vue";

createApp(App).component('svg-icon', IconSvg).mount('#app')
Copy the code

Use it like this:

<! -- home.svg -->
<svg-icon name="home"/>
Copy the code

summary

This solves the problem, you can dynamically import SVG and generate Sprite images, but it’s a bit unelegant and a bit opportunistic

This article reprints my personal blog: how to use dynamic imported SVG-sprite-cloud with original blog (halberd.cn) under vite+vue3.