fix(nuxt): enable suspensible behaviour for nested pages (#20777)

This commit is contained in:
Daniel Roe 2023-05-11 18:57:18 +01:00 committed by GitHub
parent d1577889ad
commit 5e74fe4e0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 129 additions and 0 deletions

View File

@ -54,6 +54,7 @@ export default defineComponent({
return _wrapIf(Transition, hasTransition && transitionProps, return _wrapIf(Transition, hasTransition && transitionProps,
wrapInKeepAlive(props.keepalive ?? routeProps.route.meta.keepalive ?? (defaultKeepaliveConfig as KeepAliveProps), h(Suspense, { wrapInKeepAlive(props.keepalive ?? routeProps.route.meta.keepalive ?? (defaultKeepaliveConfig as KeepAliveProps), h(Suspense, {
suspensible: true,
onPending: () => nuxtApp.callHook('page:start', routeProps.Component), onPending: () => nuxtApp.callHook('page:start', routeProps.Component),
onResolve: () => { nextTick(() => nuxtApp.callHook('page:finish', routeProps.Component).finally(done)) } onResolve: () => { nextTick(() => nuxtApp.callHook('page:finish', routeProps.Component).finally(done)) }
}, { default: () => h(RouteProvider, { key, routeProps, pageKey: key, hasTransition } as {}) }) }, { default: () => h(RouteProvider, { key, routeProps, pageKey: key, hasTransition } as {}) })

View File

@ -891,6 +891,36 @@ describe('deferred app suspense resolve', () => {
}) })
}) })
describe('nested suspense', () => {
const navigations = [
['/suspense/sync-1/async-1/', '/suspense/sync-2/async-1/'],
['/suspense/sync-1/sync-1/', '/suspense/sync-2/async-1/'],
['/suspense/async-1/async-1/', '/suspense/async-2/async-1/'],
['/suspense/async-1/sync-1/', '/suspense/async-2/async-1/']
]
it.each(navigations)('should navigate from %s to %s with no white flash', async (start, nav) => {
const page = await createPage(start, {})
await page.waitForLoadState('networkidle')
const slug = nav.replace(/[/-]+/g, '-')
await page.click(`[href^="${nav}"]`)
const text = await page.waitForFunction(slug => document.querySelector(`#${slug}`)?.innerHTML, slug)
// @ts-expect-error TODO: fix upstream in playwright - types for evaluate are broken
.then(r => r.evaluate(r => r))
// expect(text).toMatchInlineSnapshot()
// const parent = await page.waitForSelector(`#${slug}`, { state: 'attached' })
// const text = await parent.innerText()
expect(text).toContain('Async child: 2 - 1')
await page.close()
})
})
// Bug #6592 // Bug #6592
describe('page key', () => { describe('page key', () => {
it('should not cause run of setup if navigation not change page key and layout', async () => { it('should not cause run of setup if navigation not change page key and layout', async () => {

22
test/fixtures/basic/pages/suspense.vue vendored Normal file
View File

@ -0,0 +1,22 @@
<script setup>
definePageMeta({
// Nested <Suspense> + <Transition> is still buggy
pageTransition: false,
layoutTransition: false
})
const links = ['sync', 'async'].flatMap(parent => [1, 2].flatMap(p => ['sync', 'async'].flatMap(child => [1, 2].map(c => `/suspense/${parent}-${p}/${child}-${c}/`))))
</script>
<template>
<div>
This exists to test synchronous transitions between nested Suspense components.
<hr>
<NuxtLink v-for="link in links" :key="link" :to="link" style="display: block;">
{{ link }}
</NuxtLink>
<hr>
<div>
<NuxtPage />
</div>
</div>
</template>

View File

@ -0,0 +1,14 @@
<script setup lang="ts">
console.log('[async]')
const route = useRoute('suspense-async-parent')
await new Promise(resolve => setTimeout(resolve, 100))
console.log('[async] running async data')
</script>
<template>
<div :id="route.path.replace(/[/-]+/g, '-')">
Async parent: {{ route.params.parent }}
<hr>
<NuxtPage />
</div>
</template>

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
console.log('[async] [async]')
const route = useRoute('suspense-async-parent-async-child')
await new Promise(resolve => setTimeout(resolve, 500))
console.log(`[async] [${route.params.parent}] [async] [${route.params.child}] running async data`)
const data = route.params
</script>
<template>
<div>
Async child: {{ route.params.parent }} - {{ route.params.child }}
<hr>
{{ data }}
</div>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
console.log('[async] [sync]')
const route = useRoute('suspense-async-parent-sync-child')
</script>
<template>
<div>
Sync child: {{ route.params.parent }} - {{ route.params.child }}
</div>
</template>

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
console.log('[sync]')
const route = useRoute('suspense-async-parent')
</script>
<template>
<div :id="route.path.replace(/[/-]+/g, '-')">
Sync parent: {{ route.params.parent }}
<hr>
<NuxtPage />
</div>
</template>

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
console.log('[sync] [async]')
const route = useRoute('suspense-async-parent-sync-child')
await new Promise(resolve => setTimeout(resolve, 500))
console.log(`[async] [${route.params.parent}] [async] [${route.params.child}] running async data`)
const data = route.params
</script>
<template>
<div>
Async child: {{ route.params.parent }} - {{ route.params.child }}
<hr>
{{ data }}
</div>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
console.log('[sync] [sync]')
const route = useRoute('suspense-sync-parent-sync-child')
</script>
<template>
<div>
Sync child: {{ route.params.parent }} - {{ route.params.child }}
</div>
</template>