nuxt-community / svg-module

Super simple svg loading module for Nuxt.js
MIT License
340 stars 35 forks source link

Not getting dynamic loading working in Nuxt 3 #95

Open mklueh opened 2 years ago

mklueh commented 2 years ago

I'm confused. What worked with Nuxt 2 does not work (or sometimes only when hot reload triggers the page refresh)

What I've tried so far:

1. Direct require in v-html

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="require(`~/assets/${icon}?raw`)"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
</script>

result:

404 Cannot find any route matching /assets/upload.svg?raw. (http://[::]:3000/__nuxt_vite_node__/module/../assets/upload.svg?raw)

2. Via await import (not according to the docs)

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="image"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
const external = props.icon.startsWith("http")

let image = (await import("../assets/" + props.icon + "?raw")).default;

</script>

result:

404 Cannot find any route matching /assets/upload.svg?raw. (http://[::]:3000/__nuxt_vite_node__/module/../assets/upload.svg?raw)

However, the odd thing is, if I comment that line out let image = (await import("../assets/" + props.icon + "?raw")).default; and let the page load, and then uncomment it, it will load the icon

3. Via import but not with await

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="image"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
const external = props.icon.startsWith("http")

let image = import("../assets/" + props.icon + "?raw");

</script>

result: shows [OBJECT PROMISE]

4. With require in script tag

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="image"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
const external = props.icon.startsWith("http")

let image = require("../assets/" + props.icon + "?raw");

</script>

result:

Cannot find module '../assets/upload.svg?raw' Require stack: - /my-project/src/atoms/BaseImage.vue

**5. With compute and require

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="image"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
const external = props.icon.startsWith("http")

let image = computed(() => {
  return require("../assets/" + props.icon + "?raw");
})

</script>

result:

Cannot find module '../assets/upload.svg?raw' Require stack: - /my-project/src/atoms/BaseImage.vue

6. With compute and import and async

<template>
  <span class="base-icon text-brand" v-if="icon" v-html="image"/>
</template>

<script setup>

const props = defineProps({
  icon: {
    type: String
  }
})
const external = props.icon.startsWith("http")

let image = computed(async () => {
    return (await import("../assets/" + props.icon + "?raw")).default;
  }
)

</script>

result: shows [OBJECT PROMISE]

I'm not sure what else I could try and I don't see a reason why this happens.

Maybe I should add, that the component is not located in the actual Nuxt application, but a Nuxt library module, and the assets are located in the library as well.

I'd appreciate any help

manuelodelain commented 2 years ago

Duplicate of #86

johannschopplich commented 1 year ago

In Nuxt 3, you don't need svg-module, but can instead create a custom component utilizing Vite's glob import:

<template>
  <span v-if="icon" class="h-[1em] w-[1em]" v-html="icon" />
</template>

<script setup lang="ts">
const props = defineProps<{
  name?: string
}>()

// Auto-load icons
const icons = Object.fromEntries(
  Object.entries(import.meta.glob('~/assets/images/*.svg', { as: 'raw' })).map(
    ([key, value]) => {
      const filename = key.split('/').pop()!.split('.').shift()
      return [filename, value]
    },
  ),
)

// Lazily load the icon
const icon = props.name && (await icons?.[props.name]?.())
</script>
rchl commented 1 year ago

Isn't Vite only used during development? Unless this feature is not Vite specific...

johannschopplich commented 1 year ago

@rchl No, Vite is used during development as the dev server and also as the bundler for production builds. Nuxt 3 is built upon Vite. As linked before, import.meta.glob is a Vite-specific feature.

rchl commented 1 year ago

It says to support both webpack and vite as bundlers. I'm not sure which one in which cases or whether it's user's choice but I guess that in some cases the suggested code wouldn't work.

johannschopplich commented 1 year ago

Good point. If you opt in for webpack in Nuxt 3, the component would not work, since the glob import is a Vite-specific feature. Honestly, the use-cases for webpack are probably little. The overall experience is just lacking behind Vite. That's why Vite is the default in Nuxt 3 as well as optimized for it. Some modules don't even support webpack in the first place (not saying that's a good thing).