Closed mrillusion closed 6 months ago
I can also replicate this using Nuxt 3.10.3 with Nitro 2.9.1
and @storyblok/nuxt@6.0.4
It looks like the issue is that the <StoryblokComponent
is rendering a dynamic component and Nuxt needs an import for the dynamic component(docs).
I ended up with a workaround that seems working. Please try and let me know.
on the page
content type component, or any component that is rendering a list of children component, instead of this:
<script setup lang="ts">
import type { CtPageStoryblok } from '~/component-types-sb'
defineProps<{ blok: CtPageStoryblok }>()
</script>
<template>
<div v-editable="blok">
<StoryblokComponent v-for="item in blok.body" :key="item._uid" :blok="item" :uuid="item._uid" />
</div>
</template>
I used this:
<script setup lang="ts">
import type { CtPageStoryblok } from '~/component-types-sb'
import { FeaturedArticlesHero } from '#components'
defineProps<{ blok: CtPageStoryblok }>()
const knownAsyncDataComponents: Record<string, any> = {
'featured-articles-hero': markRaw(FeaturedArticlesHero),
}
function getComponent(item: any) {
const comp = knownAsyncDataComponents[item.component]
return {
...item,
component: comp || item.component,
}
}
</script>
<template>
<div v-editable="blok">
<StoryblokComponent v-for="item in blok.body" :key="item._uid" :blok="getComponent(item)" />
</div>
</template>
Here is what /component/FeaturedArticlesHero.vue
looks like. Note that it doesn't matter if you fetch from StoryBlock API directly or use useAsyncStoryBlok
or just a simple fetch to jsonplaceholder. The problem seems to be the dynamic components.
<script setup lang="ts">
import type { FeaturedArticlesHeroStoryblok } from '~/component-types-sb'
const props = defineProps<{
blok: FeaturedArticlesHeroStoryblok
}>()
const { data } = await useAsyncData(
`testing----${props.blok._uid}`,
() => $fetch('https://jsonplaceholder.typicode.com/todos/1'),
)
</script>
<template>
<div>
Featured Articles Hero
<pre>{{ data }}</pre>
</div>
</template>
Hi @Youhan I attempted this workaround and I am not seeing any changes/difference.
I did update it somewhat to handle any component name rather than individually importing all components.
// Page.vue
<script setup>
const props = defineProps({
blok: {
type: Object,
required: true,
},
})
const knownAsyncDataComponents = markRaw(props.blok.component);
function getComponent(item) {
const AsyncComponent = knownAsyncDataComponents[item.component]
return {
...item,
component: AsyncComponent || item.component,
}
}
</script>
<template>
<div v-editable="props.blok">
<component
v-for="item in props.blok.body"
:key="item._uid"
:is="getComponent(item).component"
:blok="item"
/>
</div>
</template>
For me the trick was to explicitly import all the components that have useAsyncData in them. Have you tried that?
But only my [...slug].vue
has useAsyncData then I pass this on to each component via props? Am I misunderstanding what you mean?
I haven't explicitly imported all components as it seemed like an anti-pattern when they are already autoimported but I'll give it a shot in the meantime. Thanks!
Yeah it is really hard to tell without sharing working code. Is it possible for you to share a minimal reproduction code?
Update as of Nuxt 3.11.0 it is possible to define pages as clientside or serverside specifically.
This means changing my [...slug].vue to [...slug].server.vue is a applicable workaround the resolves this issue. Note this is only possible in Nuxt 3.11.0 and higher.
It's still likely worthwhile to have this issue looked into by Storyblok to ensure their docs work as expected without these redundant API requests.
Thanks for the detailed explanation @mrillusion I will take a look
Hi @mrillusion I just opened a PR to solve this when using useAsyncStoryblok
, let me explain why this was happening.
The composable useAsyncStoryblok
uses the useStoryblokApi();
under the hood, this means that it uses isomorphic-fetch
If you check the Nuxt docs on data fetching https://nuxt.com/docs/getting-started/data-fetching you will see the following warning
[!WARNING] Beware that using only $fetch will not provide network calls de-duplication and navigation prevention. It is recommended to use $fetch for client-side interactions (event based) or combined with useAsyncData when fetching the initial component data.
Since $fetch is similar to isomorphic-fetch
is the reason why the duplication happens. So whenever you use the raw storybook API wrap it with useAsyncData
in a similar way as I did on the PR for the composable useAsyncStoryblok
Hope this helps.
:tada: This issue has been resolved in version 6.0.8 :tada:
The release is available on:
Your semantic-release bot :package::rocket:
Hi @alvarosabu thank you for taking a look into this.
I have updated my Stackblitz reproduction to use storyblok-nuxt 6.0.10 and it appears the issue still exists with no change? Am I missing something?
https://stackblitz.com/edit/nuxt-storyblok-fetch-issue?file=package.json
hi @mrillusion I would suggest trying locally? Stackblitz may do the network requests when generating the project.
I also added a StoryblokComponent
to the [slug].vue
template and getting the same result as the video
I attached a video of a statically generated Nuxt project, when you navigate to a route using useAsyncStoryblok
the network call doesn't appear in the devtools anymore. Please let me know if it's not like that on your side.
https://github.com/storyblok/storyblok-nuxt/assets/4699008/645777f3-31ae-4eb4-af60-bccbe4596d92
Hi @alvarosabu thanks for your follow up. Downloading the stackblitz example to my local machine results in the same behaviour as shown directly in stackblitz (See attached gif).
There must be a difference between your example and the reproduction I have shared via stackblitz, are you able to review the repoduction I have shared or provide your working example so I can review?
Thanks, @mrillusion I'm going to download your stackblitz and see if I can find the reason.
Hi @mrillusion I'm still investigating the issue, I can only reproduce it if I download your Stackblitz reproduction, I tested the same scenario (A page with a Teaser/Feature with a NuxtLink inside) on a project of mine and also a new fresh project and the error doesn't occur 🫠. I'm trying to figure out what's different between them.
What I noticed is that in your reproduction is that even if we follow Nuxt recommended useFetch
for de-duplication on SSG and create the raw call without the Storyblok module, it has the same issue.
I also discovered that only happens if the navigation inside the blok component is a NuxtLink, if you change it for a normal anchor it doesn't happen.
Hi, @mrillusion I finally have some updates, so I forked the Stackblitz reproduction you provided and removed the Storyblok Module completely to use Nuxt useFetch
instead.
The issue persists, if you navigate using the NuxtLink
it does a request. If you navigate with a normal anchor it doesn't.
It seems to be an upstream issue. I would suggest trying it out in a fresh nuxt install project since we weren't able to reproduce it anywhere but in the reproduction link you provided.
https://stackblitz.com/edit/nuxt-storyblok-fetch-issue-db6het?file=pages%2F%5B...slug%5D.vue
Describe the issue you're facing
I have tried various methods to statically generate nuxt 3 pages from storyblok but every method I try results in fetch requests on route change (when visiting a route for the first time).
I have tried using the 'useStoryblokApi' method
I have also tried the 'useAsyncStoryblok' shorthand (see repoduction on stackblitz for example).
Neither work?
Reproduction
https://stackblitz.com/edit/nuxt-storyblok-fetch-issue?file=pages%2F%5B...slug%5D.vue
Steps to reproduce
In the gif below I have recorded an example showing the issue.
Current Behaviour - On first visit of a page via route change a fetch request is made when the page has been prerendered
Expected Behaviour - On any visit to a page via route change or otherwise no fetch request should be made.
System Info
Used Package Manager
pnpm
Error logs (Optional)
No response
Validations