fix(nuxt): handle async children in ClientFallback (#24086)

This commit is contained in:
webfansplz 2023-11-04 05:04:26 +08:00 committed by GitHub
parent 6c48f8b8e6
commit ea3ce937e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 3 deletions

View File

@ -1,6 +1,9 @@
import { defineComponent, getCurrentInstance, onErrorCaptured, ref } from 'vue' import { defineComponent, getCurrentInstance, onErrorCaptured, ref } from 'vue'
import { ssrRenderAttrs, ssrRenderSlot, ssrRenderVNode } from 'vue/server-renderer' import { ssrRenderAttrs, ssrRenderSlot, ssrRenderVNode } from 'vue/server-renderer'
// eslint-disable-next-line
import { isPromise } from '@vue/shared'
import { useState } from '../composables/state' import { useState } from '../composables/state'
import { useNuxtApp } from '../nuxt'
import { createBuffer } from './utils' import { createBuffer } from './utils'
const NuxtClientFallbackServer = defineComponent({ const NuxtClientFallbackServer = defineComponent({
@ -34,9 +37,10 @@ const NuxtClientFallbackServer = defineComponent({
return true return true
} }
}, },
setup (props, ctx) { async setup (props, ctx) {
const vm = getCurrentInstance() const vm = getCurrentInstance()
const ssrFailed = ref(false) const ssrFailed = ref(false)
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => { onErrorCaptured((err) => {
useState(`${props.uid}`, () => true) useState(`${props.uid}`, () => true)
@ -53,10 +57,15 @@ const NuxtClientFallbackServer = defineComponent({
ssrRenderVNode(ssrVNodes.push, defaultSlot![i], vm!) ssrRenderVNode(ssrVNodes.push, defaultSlot![i], vm!)
} }
const buffer = ssrVNodes.getBuffer()
if (buffer.hasAsync) {
await Promise.all(buffer.filter(isPromise))
}
return { ssrFailed, ssrVNodes } return { ssrFailed, ssrVNodes }
} catch (ssrError) { } catch (ssrError) {
// catch in dev // catch in dev
useState(`${props.uid}`, () => true) nuxtApp.runWithContext(() => useState(`${props.uid}`, () => true))
ctx.emit('ssr-error', ssrError) ctx.emit('ssr-error', ssrError)
return { ssrFailed: true, ssrVNodes: [] } return { ssrFailed: true, ssrVNodes: [] }
} }

View File

@ -424,7 +424,8 @@ describe('pages', () => {
'clientfallback-non-stateful-setup', 'clientfallback-non-stateful-setup',
'clientfallback-non-stateful', 'clientfallback-non-stateful',
'clientfallback-stateful-setup', 'clientfallback-stateful-setup',
'clientfallback-stateful' 'clientfallback-stateful',
'clientfallback-async-setup'
] ]
const html = await $fetch('/client-fallback') const html = await $fetch('/client-fallback')
// ensure failed components are not rendered server-side // ensure failed components are not rendered server-side
@ -441,6 +442,9 @@ describe('pages', () => {
expect(html).not.toContain('<p></p>') expect(html).not.toContain('<p></p>')
expect(html).toContain('hi') expect(html).toContain('hi')
// aysnc setup
expect(html).toContain('Work with async setup')
const { page, pageErrors } = await renderPage('/client-fallback') const { page, pageErrors } = await renderPage('/client-fallback')
// ensure components reactivity once mounted // ensure components reactivity once mounted
await page.locator('#increment-count').click() await page.locator('#increment-count').click()

View File

@ -0,0 +1,14 @@
<script setup>
async function getData () { }
await getData()
// break server-side
const data = window.__NUXT__
</script>
<template>
<div>
This breaks in server-side async setup. {{ data.serverRendered }}
</div>
</template>

View File

@ -0,0 +1,11 @@
<template>
<div>
async setup
<NuxtClientFallback>
<BreakInAsyncSetup class="clientfallback-async-setup" />
<template #fallback>
<div>Work with async setup</div>
</template>
</NuxtClientFallback>
</div>
</template>

View File

@ -38,6 +38,7 @@
<ClientFallbackStatefulSetup /> <ClientFallbackStatefulSetup />
<ClientFallbackNonStatefulSetup /> <ClientFallbackNonStatefulSetup />
<ClientFallbackNonStateful /> <ClientFallbackNonStateful />
<ClientFallbackAsyncSetup />
<NuxtClientFallback keep-fallback> <NuxtClientFallback keep-fallback>
<div> <div>
<BreakInSetup /> <BreakInSetup />