mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
fix(nuxt): render a div when client-only hydrates w/o element (#23899)
This commit is contained in:
parent
8c77ce81b9
commit
a037512562
@ -40,7 +40,7 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
|||||||
? createElementVNode(res.type, res.props, res.children, res.patchFlag, res.dynamicProps, res.shapeFlag)
|
? createElementVNode(res.type, res.props, res.children, res.patchFlag, res.dynamicProps, res.shapeFlag)
|
||||||
: h(res)
|
: h(res)
|
||||||
} else {
|
} else {
|
||||||
const fragment = getFragmentHTML(ctx._.vnode.el ?? null)
|
const fragment = getFragmentHTML(ctx._.vnode.el ?? null) ?? ['<div></div>']
|
||||||
return process.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.$attrs ?? ctx._.attrs)
|
return process.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.$attrs ?? ctx._.attrs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
|||||||
? createElementVNode(res.type, res.props, res.children, res.patchFlag, res.dynamicProps, res.shapeFlag)
|
? createElementVNode(res.type, res.props, res.children, res.patchFlag, res.dynamicProps, res.shapeFlag)
|
||||||
: h(res)
|
: h(res)
|
||||||
} else {
|
} else {
|
||||||
const fragment = getFragmentHTML(instance?.vnode.el ?? null)
|
const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ['<div></div>']
|
||||||
return process.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.attrs)
|
return process.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.attrs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,10 +73,10 @@ export default defineComponent({
|
|||||||
|
|
||||||
const ssrHTML = ref<string>('')
|
const ssrHTML = ref<string>('')
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
const renderedHTML = getFragmentHTML(instance.vnode?.el ?? null).join('')
|
const renderedHTML = getFragmentHTML(instance.vnode?.el ?? null)?.join('') ?? ''
|
||||||
if (renderedHTML && nuxtApp.isHydrating) {
|
if (renderedHTML && nuxtApp.isHydrating) {
|
||||||
setPayload(`${props.name}_${hashId.value}`, {
|
setPayload(`${props.name}_${hashId.value}`, {
|
||||||
html: getFragmentHTML(instance.vnode?.el ?? null, true).join(''),
|
html: getFragmentHTML(instance.vnode?.el ?? null, true)?.join('') ?? '',
|
||||||
state: {},
|
state: {},
|
||||||
head: {
|
head: {
|
||||||
link: [],
|
link: [],
|
||||||
|
@ -104,7 +104,7 @@ export function vforToArray (source: any): any[] {
|
|||||||
* @param withoutSlots purge all slots from the HTML string retrieved
|
* @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
|
* @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): string[] {
|
export function getFragmentHTML (element: RendererNode | null, withoutSlots = false): string[] | null {
|
||||||
if (element) {
|
if (element) {
|
||||||
if (element.nodeName === '#comment' && element.nodeValue === '[') {
|
if (element.nodeName === '#comment' && element.nodeValue === '[') {
|
||||||
return getFragmentChildren(element, [], withoutSlots)
|
return getFragmentChildren(element, [], withoutSlots)
|
||||||
@ -116,7 +116,7 @@ export function getFragmentHTML (element: RendererNode | null, withoutSlots = fa
|
|||||||
}
|
}
|
||||||
return [element.outerHTML]
|
return [element.outerHTML]
|
||||||
}
|
}
|
||||||
return []
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFragmentChildren (element: RendererNode | null, blocks: string[] = [], withoutSlots = false) {
|
function getFragmentChildren (element: RendererNode | null, blocks: string[] = [], withoutSlots = false) {
|
||||||
|
@ -362,6 +362,13 @@ describe('pages', () => {
|
|||||||
|
|
||||||
expect(pageErrors).toEqual([])
|
expect(pageErrors).toEqual([])
|
||||||
await page.close()
|
await page.close()
|
||||||
|
// don't expect any errors or warning on client-side navigation
|
||||||
|
const { page: page2, consoleLogs: consoleLogs2 } = await renderPage('/')
|
||||||
|
await page2.locator('#to-client-only-components').click()
|
||||||
|
// force wait for a few ticks
|
||||||
|
await page2.waitForTimeout(50)
|
||||||
|
expect(consoleLogs2.some(log => log.type === 'error' || log.type === 'warning')).toBeFalsy()
|
||||||
|
await page2.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('/wrapper-expose/layout', async () => {
|
it('/wrapper-expose/layout', async () => {
|
||||||
|
3
test/fixtures/basic/pages/index.vue
vendored
3
test/fixtures/basic/pages/index.vue
vendored
@ -32,6 +32,9 @@
|
|||||||
<NuxtLink to="/chunk-error" :prefetch="false">
|
<NuxtLink to="/chunk-error" :prefetch="false">
|
||||||
Chunk error
|
Chunk error
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
<NuxtLink id="to-client-only-components" to="/client-only-components">
|
||||||
|
createClientOnly()
|
||||||
|
</NuxtLink>
|
||||||
<NuxtLink id="middleware-abort-non-fatal" to="/middleware-abort-non-fatal" :prefetch="false">
|
<NuxtLink id="middleware-abort-non-fatal" to="/middleware-abort-non-fatal" :prefetch="false">
|
||||||
Middleware abort navigation
|
Middleware abort navigation
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
Loading…
Reference in New Issue
Block a user