With the new Vue3 Suspense component, it seems like image loading with skeleton loaders as a default should be quite straight forward. For example, I want to have a default skeleton loader that switches to the real image when it’s been loaded like this:
<div class="w-40">
<Suspense>
<AsyncImageLoader :image-url="photoUrl" />
<template #fallback>
<SkeletonLoader shape="circle" class="mx-7 h-[106px]" />
</template>
</Suspense>
</div>
And then I define my AsyncImageLoader
like this:
<template>
<img
:src="imageUrl"
alt="Profile Photo"
style="filter: invert(0); clip-path: circle(38%)"
/>
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({
name: "AsyncImageLoader",
props: {
imageUrl: {
type: String,
required: true
}
},
async setup(props) {}
})
</script>
However this never loads, I believe because there is no promise resolved in the async setup(). If I try to fetch the image URL in the async setup() call I get a CORS error which also makes sense.
Is there a way to easily load images like this? It seems like it should be straight forward with Suspense, but manipulating html tag attributes seem more complicated than just updating the Virtual DOM based on an async action.
2
Answers
After playing around with this for a while, I've determined there isn't an easy way do what I want. Unfortunately Suspense defers component rendering until the async event completes which means waiting on an HTMLEvent for a template tag will never work. I think there are 2 primary options around this:
Perform a
fetch()
request to grab the image and then update theimg
element after the fetch request has completed successfully. I think this is the preferred solution but the downside is your server has to serve the image in a CORS friendly way as theSec-Fetch-Dest
header will beempty
in the Fetch request. I didn't want to modify theAccess-Control-Allow-Origin
header to get around this.Create a new img element and append it to the DOM. This is the method I ended up going for with the code down below:
All in all, I'll still probably stick to my first attempt at this and not even use suspense and instead use conditional rendering and a variable which is updated by the v-bind load event. This just makes more sense to me.
To me, you have the good answer to your problem.
If the methods setup have nothing it say that this is never resolved.
So you have to resolve your promise, what you want is then the image load display it.
You need to do a fetch on the url of the image, and this work.