jpkleemans / vite-svg-loader

Vite plugin to load SVG files as Vue components
MIT License
557 stars 59 forks source link

How can we dynamically import svg files? #19

Closed MuzafferDede closed 2 years ago

MuzafferDede commented 3 years ago

I am not sure if this loader supports this:

  computed: {
    currentIcon() {
        return () => import('path/to/svg/file.svg');
    }
  }

and then we can use as

<template>
  <compoment
    :is="currentIcon"
    :aria-hidden="hidden"
  />
</template>

Currently console output as

runtime-core.esm-bundler.js:38 [Vue warn]: Invalid VNode type: undefined (undefined) 
  at <Anonymous is=fn aria-hidden=true > 
  at <UiIcon name="notification" > 
jrafaaael commented 3 years ago

Hello! I was trying to do something similar and after several ideas that occurred to me, I managed to do it this way:

  1. Put in assets/icons all the svg that you will use. Create a file index.js and import and export the svg
    
    // src/assets/icons/index.js
    import add from './add.svg'
    import multiply from './multiply.svg'
    // etc

export { add, multiply, // etc }

2. In path `src/components`, create component `Icon.vue`:

// src/components/Icon.vue

3. Use it!


IMPORTANT NOTE: If your `svg` has a name in `kebab-case` (i. e `"heart-pulse.svg"`), when you use the `icon` component, the prop name must be in `snake_case` (i. e `"heart_pulse"`)
MuzafferDede commented 3 years ago

Haha, i end up almost the same way... the down side of this is that we have to include all svg files even we don't use them on the current page. well, for now this is the way i guess. Cheers!

jpkleemans commented 2 years ago

@MuzafferDede you could use something like this to support lazy loading:

<template>
  <component :is="currentIcon" />
</template>

<script>
export default {
  data () {
    return {
      currentIcon: null
    }
  },
  async created () {
    this.currentIcon = await import('./assets/Bananas.svg')
  }
}
</script>
MuzafferDede commented 2 years ago

@MuzafferDede you could use something like this to support lazy loading:

This still does not work. Have you tried?

pmatisko commented 2 years ago

Guys, you may try this plugin:

https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars

It works well on localdev, but I haven't figured out how to use it on production.

jpkleemans commented 2 years ago

@MuzafferDede you could use something like this to support lazy loading:

This still does not work. Have you tried?

Yes, it works for me (using vite v2.4.4)

MuzafferDede commented 2 years ago

@jpkleemans i get Cannot convert object to primitive value when i use your suggestion.

I come out with this which works

import { defineAsyncComponent } from 'vue'
export default {
  props: {
    name: {
      type: String,
      default: undefined,
    },
  },
  computed: {
    currentIcon() {
      return defineAsyncComponent(() => import(`../../assets/icons/svg/${this.name}.svg`))
    },
  },
};
jpkleemans commented 2 years ago

@MuzafferDede ah, that's an even better solution!

Eternal-Rise commented 2 years ago

Have some solution for vue-2?

nicooprat commented 2 years ago

You can do it with Vue 2 thanks to the @vue/composition-api package 👍

import { defineAsyncComponent } from '@vue/composition-api';
JacobRex commented 1 year ago

For those wanting to use <script setup> you can do this. This worked for me on Nuxt3.

<template>
  <component :is="icon"/>
</template>

<script lang="ts" setup>
const props = defineProps({
  name: {
    type: String,
    required: true
  },
});

const icon = defineAsyncComponent(() => import(`../../assets/icons/${props.name}.svg`));
</script>
alexyca commented 1 year ago

how could you make it work to have a :src instead of an :is ?

9mm commented 1 year ago

Just to be clear, if I use defineAsyncComponent, is that going to try to load everything via URL then? Or will it figure it out at build time?

I wanted to retain the component style where the SVG's are actually inlined

Alex-Kozynko commented 10 months ago

I think I found the best beautiful solution :) Vite docs: https://vitejs.dev/guide/features.html#glob-import

Template:

<component :is="icon" :class="className" />

Script:

import { defineAsyncComponent } from 'vue'

export default {
  props: {
    name: {
      type: String,
      required: true
    }
  },

  data() {
    return {
      icons: import.meta.glob(`./**/*.svg`)
    }
  },

  computed: {
    icon() {
      return defineAsyncComponent(() => this.icons[`./${this.name}.svg`]())
    },
  }
}
troyere commented 7 months ago

This is not a real dynamic import : import.meta.glob('./**/*.svg') clearly say "I will import ALL icons according the glob pattern", and that is not what we want in real dynamic import.

Alex-Kozynko commented 7 months ago

This is not a real dynamic import : import.meta.glob('./**/*.svg') clearly say "I will import ALL icons according the glob pattern", and that is not what we want in real dynamic import.

Please write documentation from my link :)