From b3087140ac0cb8beb4075528057508e4b3461e5b Mon Sep 17 00:00:00 2001 From: julien huang Date: Fri, 18 Aug 2023 20:49:54 +0200 Subject: [PATCH] fix: fix payload and use reviver to partially retrieve data --- .../nuxt/src/app/components/nuxt-island.ts | 63 ++++++++----------- .../src/app/plugins/revive-payload.client.ts | 12 +++- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/packages/nuxt/src/app/components/nuxt-island.ts b/packages/nuxt/src/app/components/nuxt-island.ts index 2396d68362..c0260e2b26 100644 --- a/packages/nuxt/src/app/components/nuxt-island.ts +++ b/packages/nuxt/src/app/components/nuxt-island.ts @@ -91,47 +91,40 @@ export default defineComponent({ key, ...(import.meta.server && import.meta.prerender) ? {} - : { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } } + : { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } }, + result: { + chunks: result.chunks, + props: result.props, + teleports: result.teleports + } }, ...result } } - const ssrHTML = ref('') - - if (import.meta.client) { - const renderedHTML = getFragmentHTML(instance.vnode?.el ?? null).join('') - if (renderedHTML && nuxtApp.isHydrating) { - setPayload(`${props.name}_${hashId.value}`, { - html: getFragmentHTML(instance.vnode?.el ?? null, true).join(''), - state: {}, - head: { - link: [], - style: [] - }, - chunks: {}, - props: {}, - teleports: {} - }) - } - ssrHTML.value = renderedHTML + // needs to be non-reactive because we don't want to trigger re-renders + // at hydration, we only retrieve props/chunks/teleports from payload. See the reviver at nuxt\src\app\plugins\revive-payload.client.ts + const rawPayload = toRaw(nuxtApp.payload.data)?.[`${props.name}_${hashId.value}`] ?? {} + const nonReactivePayload: Pick = { + chunks: rawPayload.chunks, + props: rawPayload.props, + teleports: rawPayload.teleports } + const ssrHTML = ref(getFragmentHTML(instance.vnode?.el ?? null).join('')) + const slotProps = computed(() => getSlotProps(ssrHTML.value)) const uid = ref(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? randomUUID()) const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1])) - // no need for reactivity - const interactivePayload: Pick = import.meta.client && nuxtApp.isHydrating ? toRaw(nuxtApp.payload.data)[`${props.name}_${hashId.value}_interactive`] : {} - const html = computed(() => { const currentSlots = Object.keys(slots) let html = ssrHTML.value - if (import.meta.client && !canLoadClientComponent.value && Object.keys(interactivePayload.teleports).length) { - for (const key in interactivePayload.teleports) { + if (import.meta.client && !canLoadClientComponent.value && Object.keys(nonReactivePayload.teleports).length) { + for (const key in nonReactivePayload.teleports) { html = html.replace(new RegExp(`
]*nuxt-ssr-client="${key}"[^>]*>`), (full) => { - return full + interactivePayload.teleports[key] + return full + nonReactivePayload.teleports[key] }) } } @@ -154,7 +147,8 @@ export default defineComponent({ async function _fetchComponent (force = false) { const key = `${props.name}_${hashId.value}` - if (nuxtApp.payload.data[key] && !force) { return nuxtApp.payload.data[key] } + + if (nuxtApp.payload.data[key]?.html && !force) { return nuxtApp.payload.data[key] } const url = remoteComponentIslands && props.source ? new URL(`/__nuxt_island/${key}`, props.source).href : `/__nuxt_island/${key}` @@ -197,19 +191,14 @@ export default defineComponent({ key.value++ error.value = null - nuxtApp.payload.data[`${props.name}_${hashId.value}_interactive`] = { - chunks: res.chunks, - props: res.props, - teleports: res.teleports - } if (import.meta.client) { if (canLoadClientComponent.value) { await loadComponents(props.source, res.chunks) } - interactivePayload.props = res.props + nonReactivePayload.props = res.props } - interactivePayload.teleports = res.teleports - interactivePayload.chunks = res.chunks + nonReactivePayload.teleports = res.teleports + nonReactivePayload.chunks = res.chunks if (import.meta.client) { // must await next tick for Teleport to work correctly with static node re-rendering @@ -237,7 +226,7 @@ export default defineComponent({ } else if (import.meta.server || !nuxtApp.isHydrating) { await fetchComponent() } else if (nuxtApp.isHydrating && canLoadClientComponent.value) { - await loadComponents(props.source, interactivePayload.chunks) + await loadComponents(props.source, nonReactivePayload.chunks) } return () => { @@ -258,14 +247,14 @@ export default defineComponent({ } } if (import.meta.server) { - for (const [id, html] of Object.entries(interactivePayload.teleports)) { + for (const [id, html] of Object.entries(nonReactivePayload.teleports)) { nodes.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, { default: () => [createStaticVNode(html, 1)] })) } } if (import.meta.client && canLoadClientComponent.value && html.value.includes('nuxt-ssr-client')) { - for (const [id, props] of Object.entries(interactivePayload.props)) { + for (const [id, props] of Object.entries(nonReactivePayload.props)) { // @ts-expect-error _ is the component's default export in build chunks const component = components!.get(id.split('-')[0])!._ ?? components!.get(id.split('-')[0])! const vnode = createVNode(Teleport, { to: `[nuxt-ssr-component-uid='${uid.value}'] [nuxt-ssr-client="${id}"]` }, { diff --git a/packages/nuxt/src/app/plugins/revive-payload.client.ts b/packages/nuxt/src/app/plugins/revive-payload.client.ts index 6361148156..d4494d5279 100644 --- a/packages/nuxt/src/app/plugins/revive-payload.client.ts +++ b/packages/nuxt/src/app/plugins/revive-payload.client.ts @@ -17,7 +17,7 @@ const revivers: Record any> = { } if (componentIslands) { - revivers.Island = ({ key, params }: any) => { + revivers.Island = ({ key, params, result }: any) => { const nuxtApp = useNuxtApp() if (!nuxtApp.isHydrating) { nuxtApp.payload.data[key] = nuxtApp.payload.data[key] || $fetch(`/__nuxt_island/${key}`, { @@ -28,7 +28,15 @@ if (componentIslands) { return r }) } - return null + return { + html: '', + state: {}, + head: { + link: [], + style: [] + }, + ...result + } } }