admin管理员组文章数量:1426855
I'm using Suspense, and fetching async data on pages. On init fallback loading indicator is visible, but while switching pages I'm keeping current page content until new page is ready + nprogress
bar, so site behaves more like SSR. There is also Transition between, but it's not important for that issue.
And it works great. Problem is when my page has child pages (nested routes), also with Suspense. When I'm on nested page like /demo/demo1
and navigate to home, content of /demo
template stays like it should, but content of subpage demo1
disappears. I need to keep nested Suspense until parent Suspense is ready. Any ideas?
I simplified code as much as possible. I'm using same <RouterView>
code on both root App and page.
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Suspense>
<ponent :is="Component" />
<template #fallback>
Loading...
</template>
</Suspense>
</template>
</RouterView>
Here is live reproduction: /pages/demo/demo1.vue (there is no nprogress, just wait 1s between pages).
Steps to reproduction:
- Click on "About" - "home page" stays until "about" is ready, and then "about" appears - works OK.
- Click on "Demo" - "demo" root page appears, fallback for nested "demo/demo1" route appears - works OK.
- Click on "Home" - content of "demo/demo1" immediately disappears.
I'm using Suspense, and fetching async data on pages. On init fallback loading indicator is visible, but while switching pages I'm keeping current page content until new page is ready + nprogress
bar, so site behaves more like SSR. There is also Transition between, but it's not important for that issue.
And it works great. Problem is when my page has child pages (nested routes), also with Suspense. When I'm on nested page like /demo/demo1
and navigate to home, content of /demo
template stays like it should, but content of subpage demo1
disappears. I need to keep nested Suspense until parent Suspense is ready. Any ideas?
I simplified code as much as possible. I'm using same <RouterView>
code on both root App and page.
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Suspense>
<ponent :is="Component" />
<template #fallback>
Loading...
</template>
</Suspense>
</template>
</RouterView>
Here is live reproduction: https://stackblitz./edit/vitejs-vite-gg6kqo?file=src/pages/demo/demo1.vue (there is no nprogress, just wait 1s between pages).
Steps to reproduction:
- Click on "About" - "home page" stays until "about" is ready, and then "about" appears - works OK.
- Click on "Demo" - "demo" root page appears, fallback for nested "demo/demo1" route appears - works OK.
- Click on "Home" - content of "demo/demo1" immediately disappears.
- 1 It might be not working yet. github./vuejs/core/issues/5637 – Kalimah Commented May 10, 2023 at 7:46
- 1 Vue v3.3.2, this now seems to be working as expected for me. – Marc Commented May 17, 2023 at 14:03
- @Marc weird. I did tested alpha version before asking here, because they implemented "suspensible" option in 3.3.0. But it changed nothing in my issue. Will test it again with 3.3.2 then, with and without that option. thanks. – chojnicki Commented May 18, 2023 at 15:04
- 1 I noticed a changelog in Nuxt3 explicitly mentioning bumping its Vue dependency version to 3.3.2 because a nested pages bug fix was released, but I saw no documented changes in the Vue 3 changelog its self for this issue. – Marc Commented May 18, 2023 at 18:45
- 1 I updated my answer further down the thread with what I am doing, perhaps there is something there that might answer this, it defiantly working for me I put in production last week soon as I spotted the discrete fix. I assume they are quite on it because its not officially production ready. – Marc Commented May 26, 2023 at 10:14
2 Answers
Reset to default 3 +200I wouldn't say this to be the best answer, but it seems to me that in order for <Suspense>
to work, its child must be an asynchronous ponent. In this case, <RouterView>
is rendered as a non-asynchronous ponent (even though its children are asynchronous ponents), thus the root <Suspense>
wouldn't wait for the <Suspense>
inside the <RouterView>
.
As a workaround, I think we can cache the ponent, so that it wouldn't look as if it's gone when the router is changed (the third case).
My method of caching is by adding this <script setup>
in the demo.vue
file:
<script setup>
import { ref } from 'vue';
const CachedComponent = ref(null);
function cacheComponent(Component) {
CachedComponent.value = Component || CachedComponent.value;
}
</script>
And then modify the <RouterView>
inside the same file to this:
...
<RouterView v-slot="{ Component }">
{{ cacheComponent(Component) }}
<template v-if="Component || CachedComponent">
<Suspense>
<ponent :is="Component || CachedComponent" />
<template #fallback>
<div style="font-size: 1.5rem">Loading Demo Nested...</div>
</template>
</Suspense>
</template>
</RouterView>
...
The {{ cacheComponent(Component) }}
line will cache the ponent when it's rendered (it's used that way because <RouterView>
doesn't have callback when its slots are mounted). The CachedComponent.value = Component || CachedComponent.value;
line will update the CachedComponent value if there is a new Component that is not falsy. And if it's falsy (meaning that it's not mounted yet or not mounted anymore), it will render the previous latest cached version of the ponent. I know this is hacky, but in my opinion, this is a very simple workaround.
This is the forked stackblitz if you're interested.
Update Vue 3.3.2 - 17/05/2023
I just tested out this version and the nested router pages with async setup
are working.
From what I can see, nested router pages pages are no longer displaying blank pages with a console warning about needing to be wrapped in Suspense
.
Instead the top level Suspense
is now handling the child pages without wrongly suggesting that there needed to be a Suspense
around the nested RouterView
.
// App.vue
<RouterView v-slot="{ Component, route }">
<template v-if="Component">
<KeepAlive>
<Suspense>
<ponent :is="Component"/>
<template #fallback>
LOADING...
</template>
</Suspense>
</KeepAlive>
</template>
</RouterView>
// pages/nested-path/index.vue
<div>
<RouterView v-slot="{ Component, route }">
<ponent :is="Component" :key="route.path"/>
</RouterView>
</div>
Previous Response
It simply does not work at this time for plex projects when you have nested routes, it works fine with routing on a single level as you have noticed.
I battled with this for a while, tried many binations and found nested suspense is broken, it is to do with a breakdown in the functionality around the key
that is passively used for the router ponent, I tried setting this manually which yielded various results and other unfixable/annoying problems including any use with keep-alive
.
Not being able to use async setup
was really annoying and unfortunate especially for suspending while negotiating other promises before attempting an API call, I especially like how clean the code would have been.
I had to settle for async watch
where I set a loading = ref(true)
state passed to a custom loading state ponent wrapper that would I/O block content and show a loading progress in the content body.
// myPage.vue
<script setup lang="ts">
const props = defineProps<{
id: string
}>()
const loading = ref(true)
watch(props, async () => {
loading.value = true
await myApi.endpoint.get(props)
loading.value = false
}, {immediate: true})
</script>
<template>
<content-loader :loading="loading">
<div>
My page content
</div>
</content-loader>
</template>
// ContentLoader.vue
<script setup lang="ts">
withDefaults(defineProps<{
loading: boolean
}>(), {
loading: true
})
</script>
<template>
<div class="d-flex fill-height justify-center align-center">
<template v-if="$props.loading">
<span>Content loading...
</template>
<template v-else>
<slot/>
</template>
</div>
</template>
You could slap transitions in there too if you wanted.
I was also thinking about re-deducing repetition of the loading wrapper and loading reference by moving it to the parent ponent hosting the child router-view
and using emit
in the child page to toggle loading. I haven't tried this, need to ensure the initial state is true when transitioning to other pages.
本文标签: javascriptSuspense in Vue 3 on nested routeswhy content disappearsStack Overflow
版权声明:本文标题:javascript - Suspense in Vue 3 on nested routes - why content disappears? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745483443a2660277.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论