]*nuxt-ssr-client="${key}"[^>]*>`), (full) => {
return full + value
@@ -184,6 +160,7 @@ export default defineComponent({
ssrHTML.value = res.html.replace(UID_ATTR, () => {
return `nuxt-ssr-component-uid="${getId()}"`
})
+ // force re-render the static content
key.value++
error.value = null
@@ -196,7 +173,8 @@ export default defineComponent({
nonReactivePayload.teleports = res.teleports
nonReactivePayload.chunks = res.chunks
- // must await next tick for Teleport to work correctly with static node re-rendering
+ // must await next tick for Teleport to work correctly so vue can teleport the content to the new static node
+ // teleport update is based on uid
await nextTick()
setUid()
@@ -230,6 +208,7 @@ export default defineComponent({
return [slots.fallback?.({ error: error.value }) ?? createVNode('div')]
}
const nodes = [createVNode(Fragment, {
+ // static nodes in build need to be keyed to force it to re-render
key: key.value
}, [h(createStaticVNode(html.value || '
', 1))])]
diff --git a/packages/nuxt/src/app/components/nuxt-island.server.ts b/packages/nuxt/src/app/components/nuxt-island.server.ts
index e83ef38d99..ab836852bb 100644
--- a/packages/nuxt/src/app/components/nuxt-island.server.ts
+++ b/packages/nuxt/src/app/components/nuxt-island.server.ts
@@ -9,40 +9,15 @@ import { joinURL, withQuery } from 'ufo'
import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer'
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
import { prerenderRoutes, useRequestEvent } from '../composables/ssr'
-import { getSlotProps } from './utils'
+import { SLOTNAME_RE, SSR_UID_RE, UID_ATTR, getSlotProps, nuxtIslandProps, pKey } from './utils'
// @ts-expect-error virtual file
import { remoteComponentIslands, selectiveClient } from '#build/nuxt.config.mjs'
-const pKey = '_islandPromises'
-const SSR_UID_RE = /nuxt-ssr-component-uid="([^"]*)"/
-const UID_ATTR = /nuxt-ssr-component-uid(="([^"]*)")?/
-const SLOTNAME_RE = /nuxt-ssr-slot-name="([^"]*)"/g
-
export default defineComponent({
name: 'NuxtIsland',
props: {
- name: {
- type: String,
- required: true
- },
- lazy: Boolean,
- props: {
- type: Object,
- default: () => undefined
- },
- context: {
- type: Object,
- default: () => ({})
- },
- source: {
- type: String,
- default: () => undefined
- },
- dangerouslyLoadClientComponents: {
- type: Boolean,
- default: false
- }
+ ...nuxtIslandProps
},
async setup(props, { slots }) {
const error = ref
(null)
@@ -52,7 +27,6 @@ export default defineComponent({
const hashId = computed(() => hash([props.name, filteredProps.value, props.context, props.source]))
const event = useRequestEvent()
-
function setPayload(key: string, result: NuxtIslandResponse) {
nuxtApp.payload.data[key] = {
__nuxt_island: {
@@ -69,11 +43,7 @@ export default defineComponent({
...result
}
}
- const nonReactivePayload: Pick = {
- chunks: {},
- props: {},
- teleports: {}
- }
+ const teleports: NuxtIslandResponse['teleports'] = {}
const ssrHTML = ref('')
@@ -116,37 +86,33 @@ export default defineComponent({
setPayload(key, result)
return result
}
-
- async function fetchComponent(force = false) {
+
+ try {
nuxtApp[pKey] = nuxtApp[pKey] || {}
if (!nuxtApp[pKey][uid.value]) {
nuxtApp[pKey][uid.value] = _fetchComponent().finally(() => {
delete nuxtApp[pKey]![uid.value]
})
}
- try {
- const res: NuxtIslandResponse = await nuxtApp[pKey][uid.value]
- cHead.value.link = res.head.link
- cHead.value.style = res.head.style
- ssrHTML.value = res.html.replace(UID_ATTR, () => {
- return `nuxt-ssr-component-uid="${randomUUID()}"`
- })
- nonReactivePayload.teleports = res.teleports
- nonReactivePayload.chunks = res.chunks
+ const res: NuxtIslandResponse = await nuxtApp[pKey][uid.value]
+ cHead.value.link = res.head.link
+ cHead.value.style = res.head.style
+ ssrHTML.value = res.html.replace(UID_ATTR, () => {
+ return `nuxt-ssr-component-uid="${randomUUID()}"`
+ })
+ Object.assign(teleports, res.teleports)
- setUid()
- } catch (e) {
- error.value = e
- }
+ setUid()
+ } catch(e) {
+ error.value = e
}
- await fetchComponent()
-
+
return () => {
if (!ssrHTML.value || error.value) {
return [slots.fallback?.({ error: error.value }) ?? createVNode('div')]
}
- const nodes = [createVNode(Fragment, {}, [h(createStaticVNode(ssrHTML.value || '', 1))])]
+ const nodes = [createVNode(Fragment, null, [h(createStaticVNode(ssrHTML.value || '', 1))])]
// render slots and teleports
if (uid.value && ssrHTML.value) {
@@ -157,7 +123,7 @@ export default defineComponent({
}))
}
}
- for (const [id, html] of Object.entries(nonReactivePayload.teleports ?? {})) {
+ for (const [id, html] of Object.entries(teleports ?? {})) {
nodes.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
default: () => [createStaticVNode(html, 1)]
}))
diff --git a/packages/nuxt/src/app/components/utils.ts b/packages/nuxt/src/app/components/utils.ts
index dbe8d23744..997d89005d 100644
--- a/packages/nuxt/src/app/components/utils.ts
+++ b/packages/nuxt/src/app/components/utils.ts
@@ -185,3 +185,32 @@ export function getSlotProps (html: string) {
}
return data
}
+
+export const pKey = '_islandPromises'
+export const SSR_UID_RE = /nuxt-ssr-component-uid="([^"]*)"/
+export const UID_ATTR = /nuxt-ssr-component-uid(="([^"]*)")?/
+export const SLOTNAME_RE = /nuxt-ssr-slot-name="([^"]*)"/g
+
+export const nuxtIslandProps = {
+ name: {
+ type: String,
+ required: true
+ },
+ lazy: Boolean,
+ props: {
+ type: Object,
+ default: () => undefined
+ },
+ context: {
+ type: Object,
+ default: () => ({})
+ },
+ source: {
+ type: String,
+ default: () => undefined
+ },
+ dangerouslyLoadClientComponents: {
+ type: Boolean,
+ default: false
+ }
+}
\ No newline at end of file