From ea3ce937e6b0d09728ec12d7c0b634651f18113d Mon Sep 17 00:00:00 2001 From: webfansplz <308241863@qq.com> Date: Sat, 4 Nov 2023 05:04:26 +0800 Subject: [PATCH] fix(nuxt): handle async children in `ClientFallback` (#24086) --- .../src/app/components/client-fallback.server.ts | 13 +++++++++++-- test/basic.test.ts | 6 +++++- .../basic/components/BreakInAsyncSetup.vue | 14 ++++++++++++++ .../basic/components/clientFallback/AsyncSetup.vue | 11 +++++++++++ test/fixtures/basic/pages/client-fallback.vue | 1 + 5 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/basic/components/BreakInAsyncSetup.vue create mode 100644 test/fixtures/basic/components/clientFallback/AsyncSetup.vue diff --git a/packages/nuxt/src/app/components/client-fallback.server.ts b/packages/nuxt/src/app/components/client-fallback.server.ts index f4229867c9..3b07745b38 100644 --- a/packages/nuxt/src/app/components/client-fallback.server.ts +++ b/packages/nuxt/src/app/components/client-fallback.server.ts @@ -1,6 +1,9 @@ import { defineComponent, getCurrentInstance, onErrorCaptured, ref } from 'vue' import { ssrRenderAttrs, ssrRenderSlot, ssrRenderVNode } from 'vue/server-renderer' +// eslint-disable-next-line +import { isPromise } from '@vue/shared' import { useState } from '../composables/state' +import { useNuxtApp } from '../nuxt' import { createBuffer } from './utils' const NuxtClientFallbackServer = defineComponent({ @@ -34,9 +37,10 @@ const NuxtClientFallbackServer = defineComponent({ return true } }, - setup (props, ctx) { + async setup (props, ctx) { const vm = getCurrentInstance() const ssrFailed = ref(false) + const nuxtApp = useNuxtApp() onErrorCaptured((err) => { useState(`${props.uid}`, () => true) @@ -53,10 +57,15 @@ const NuxtClientFallbackServer = defineComponent({ ssrRenderVNode(ssrVNodes.push, defaultSlot![i], vm!) } + const buffer = ssrVNodes.getBuffer() + if (buffer.hasAsync) { + await Promise.all(buffer.filter(isPromise)) + } + return { ssrFailed, ssrVNodes } } catch (ssrError) { // catch in dev - useState(`${props.uid}`, () => true) + nuxtApp.runWithContext(() => useState(`${props.uid}`, () => true)) ctx.emit('ssr-error', ssrError) return { ssrFailed: true, ssrVNodes: [] } } diff --git a/test/basic.test.ts b/test/basic.test.ts index 4e5d42b3d6..10455df657 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -424,7 +424,8 @@ describe('pages', () => { 'clientfallback-non-stateful-setup', 'clientfallback-non-stateful', 'clientfallback-stateful-setup', - 'clientfallback-stateful' + 'clientfallback-stateful', + 'clientfallback-async-setup' ] const html = await $fetch('/client-fallback') // ensure failed components are not rendered server-side @@ -441,6 +442,9 @@ describe('pages', () => { expect(html).not.toContain('

') expect(html).toContain('hi') + // aysnc setup + expect(html).toContain('Work with async setup') + const { page, pageErrors } = await renderPage('/client-fallback') // ensure components reactivity once mounted await page.locator('#increment-count').click() diff --git a/test/fixtures/basic/components/BreakInAsyncSetup.vue b/test/fixtures/basic/components/BreakInAsyncSetup.vue new file mode 100644 index 0000000000..8212300848 --- /dev/null +++ b/test/fixtures/basic/components/BreakInAsyncSetup.vue @@ -0,0 +1,14 @@ + + + diff --git a/test/fixtures/basic/components/clientFallback/AsyncSetup.vue b/test/fixtures/basic/components/clientFallback/AsyncSetup.vue new file mode 100644 index 0000000000..4321cadf28 --- /dev/null +++ b/test/fixtures/basic/components/clientFallback/AsyncSetup.vue @@ -0,0 +1,11 @@ + diff --git a/test/fixtures/basic/pages/client-fallback.vue b/test/fixtures/basic/pages/client-fallback.vue index 9b4d7c5db8..39c526d45f 100644 --- a/test/fixtures/basic/pages/client-fallback.vue +++ b/test/fixtures/basic/pages/client-fallback.vue @@ -38,6 +38,7 @@ +