Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(nuxt): reduce nuxt island payload #26569

Merged
merged 2 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 35 additions & 28 deletions packages/nuxt/src/app/components/nuxt-island.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,30 +88,32 @@ export default defineComponent({
onMounted(() => { mounted.value = true; teleportKey.value++ })

function setPayload (key: string, result: NuxtIslandResponse) {
const toRevive: Partial<NuxtIslandResponse> = {}
if (result.props) { toRevive.props = result.props }
if (result.slots) { toRevive.slots = result.slots }
if (result.components) { toRevive.components = result.components }

nuxtApp.payload.data[key] = {
__nuxt_island: {
key,
...(import.meta.server && import.meta.prerender)
? {}
: { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } },
result: {
props: result.props,
slots: result.slots,
components: result.components
}
result: toRevive
},
...result
}
}

const payloads: Required<Pick<NuxtIslandResponse, 'slots' | 'components'>> = {
slots: {},
components: {}
}
const payloads: Partial<Pick<NuxtIslandResponse, 'slots' | 'components'>> = {}

if (instance.vnode.el) {
payloads.slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots ?? {}
payloads.components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components ?? {}
const slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots
if (slots) { payloads.slots = slots }
if (selectiveClient) {
const components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components
if (components) { payloads.components = components }
}
}

const ssrHTML = ref<string>('')
Expand All @@ -137,12 +139,15 @@ export default defineComponent({
}
}

return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
if (!currentSlots.includes(slotName)) {
return full + (payloads.slots[slotName]?.fallback || '')
}
return full
})
if (payloads.slots) {
return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
if (!currentSlots.includes(slotName)) {
return full + (payloads.slots?.[slotName]?.fallback || '')
}
return full
})
}
return html
})

const cHead = ref<Record<'link' | 'style', Array<Record<string, string>>>>({ link: [], style: [] })
Expand Down Expand Up @@ -256,24 +261,26 @@ export default defineComponent({
teleports.push(createVNode(Teleport,
// use different selectors for even and odd teleportKey to force trigger the teleport
{ to: import.meta.client ? `${isKeyOdd ? 'div' : ''}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` },
{ default: () => (payloads.slots[slot].props?.length ? payloads.slots[slot].props : [{}]).map((data: any) => slots[slot]?.(data)) })
{ default: () => (payloads.slots?.[slot].props?.length ? payloads.slots[slot].props : [{}]).map((data: any) => slots[slot]?.(data)) })
)
}
}
if (selectiveClient) {
if (import.meta.server) {
for (const [id, info] of Object.entries(payloads.components ?? {})) {
const { html, slots } = info
let replaced = html.replaceAll('data-island-uid', `data-island-uid="${uid.value}"`)
for (const slot in slots) {
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, full => full + slots[slot])
if (payloads.components) {
for (const [id, info] of Object.entries(payloads.components)) {
const { html, slots } = info
let replaced = html.replaceAll('data-island-uid', `data-island-uid="${uid.value}"`)
for (const slot in slots) {
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, full => full + slots[slot])
}
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
default: () => [createStaticVNode(replaced, 1)]
}))
}
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
default: () => [createStaticVNode(replaced, 1)]
}))
}
} else if (canLoadClientComponent.value) {
for (const [id, info] of Object.entries(payloads.components ?? {})) {
} else if (canLoadClientComponent.value && payloads.components) {
for (const [id, info] of Object.entries(payloads.components)) {
const { props, slots } = info
const component = components!.get(id)!
// use different selectors for even and odd teleportKey to force trigger the teleport
Expand Down
4 changes: 2 additions & 2 deletions packages/nuxt/src/core/runtime/nitro/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ const SSR_CLIENT_TELEPORT_MARKER = /^uid=([^;]*);client=(.*)$/
const SSR_CLIENT_SLOT_MARKER = /^island-slot=(?:[^;]*);(.*)$/

function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['slots'] {
if (!ssrContext.islandContext) { return {} }
if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.slots).length) { return undefined }
const response: NuxtIslandResponse['slots'] = {}
for (const slot in ssrContext.islandContext.slots) {
response[slot] = {
Expand All @@ -661,7 +661,7 @@ function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse[
}

function getClientIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['components'] {
if (!ssrContext.islandContext) { return {} }
if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.components).length) { return undefined }
const response: NuxtIslandResponse['components'] = {}

for (const clientUid in ssrContext.islandContext.components) {
Expand Down
3 changes: 0 additions & 3 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1968,14 +1968,12 @@ describe('component islands', () => {

expect(result).toMatchInlineSnapshot(`
{
"components": {},
"head": {
"link": [],
"style": [],
},
"html": "<pre data-island-uid> Route: /foo
</pre>",
"slots": {},
"state": {},
}
`)
Expand All @@ -1993,7 +1991,6 @@ describe('component islands', () => {
result.html = result.html.replaceAll(/ (data-island-uid|data-island-component)="([^"]*)"/g, '')
expect(result).toMatchInlineSnapshot(`
{
"components": {},
"head": {
"link": [],
"style": [],
Expand Down