perf(nuxt): prepopulate island payloads from rendered html (#22049)

Co-authored-by: Julien Huang <julien.huang@outlook.fr>
This commit is contained in:
Daniel Roe 2023-07-12 07:28:22 +01:00 committed by GitHub
parent 08acfb749d
commit dcef9d94cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 20 deletions

View File

@ -48,7 +48,33 @@ export default defineComponent({
const mounted = ref(false) const mounted = ref(false)
onMounted(() => { mounted.value = true }) onMounted(() => { mounted.value = true })
const ssrHTML = ref<string>(process.client ? getFragmentHTML(instance.vnode?.el ?? null).join('') ?? '<div></div>' : '<div></div>') function setPayload (key: string, result: NuxtIslandResponse) {
nuxtApp.payload.data[key] = {
__nuxt_island: {
key,
...(process.server && process.env.prerender)
? {}
: { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } }
},
...result
}
}
const ssrHTML = ref('<div></div>')
if (process.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: []
}
})
}
ssrHTML.value = renderedHTML ?? '<div></div>'
}
const slotProps = computed(() => getSlotProps(ssrHTML.value)) const slotProps = computed(() => getSlotProps(ssrHTML.value))
const uid = ref<string>(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? randomUUID()) const uid = ref<string>(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? randomUUID())
const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1])) const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1]))
@ -91,20 +117,7 @@ export default defineComponent({
appendResponseHeader(event, 'x-nitro-prerender', hints) appendResponseHeader(event, 'x-nitro-prerender', hints)
} }
} }
nuxtApp.payload.data[key] = { setPayload(key, result)
__nuxt_island: {
key,
...(process.server && process.env.prerender)
? {}
: {
params: {
...props.context,
props: props.props ? JSON.stringify(props.props) : undefined
}
}
},
...result
}
return result return result
} }
const key = ref(0) const key = ref(0)

View File

@ -99,25 +99,42 @@ export function vforToArray (source: any): any[] {
return [] return []
} }
export function getFragmentHTML (element: RendererNode | null) { /**
* Retrieve the HTML content from an element
* Handles `<!--[-->` Fragment elements
*
* @param element the element to retrieve the HTML
* @param withoutSlots purge all slots from the HTML string retrieved
* @returns {string[]} An array of string which represent the content of each element. Use `.join('')` to retrieve a component vnode.el HTML
*/
export function getFragmentHTML (element: RendererNode | null, withoutSlots = false) {
if (element) { if (element) {
if (element.nodeName === '#comment' && element.nodeValue === '[') { if (element.nodeName === '#comment' && element.nodeValue === '[') {
return getFragmentChildren(element) return getFragmentChildren(element, [], withoutSlots)
}
if (withoutSlots) {
const clone = element.cloneNode(true)
clone.querySelectorAll('[nuxt-ssr-slot-name]').forEach((n: Element) => { n.innerHTML = '' })
return [clone.outerHTML]
} }
return [element.outerHTML] return [element.outerHTML]
} }
return [] return []
} }
function getFragmentChildren (element: RendererNode | null, blocks: string[] = []) { function getFragmentChildren (element: RendererNode | null, blocks: string[] = [], withoutSlots = false) {
if (element && element.nodeName) { if (element && element.nodeName) {
if (isEndFragment(element)) { if (isEndFragment(element)) {
return blocks return blocks
} else if (!isStartFragment(element)) { } else if (!isStartFragment(element)) {
blocks.push(element.outerHTML) const clone = element.cloneNode(true) as Element
if (withoutSlots) {
clone.querySelectorAll('[nuxt-ssr-slot-name]').forEach((n) => { n.innerHTML = '' })
}
blocks.push(clone.outerHTML)
} }
getFragmentChildren(element.nextSibling, blocks) getFragmentChildren(element.nextSibling, blocks, withoutSlots)
} }
return blocks return blocks
} }