mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
fix(nuxt): overwrite island payload instead of merging (#25299)
This commit is contained in:
parent
92ba515549
commit
a57b428587
@ -104,12 +104,15 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const payloadSlots: NonNullable<NuxtIslandResponse['slots']> = {}
|
const payloads: Required<Pick<NuxtIslandResponse, 'slots' | 'components'>> = {
|
||||||
const payloadComponents: NonNullable<NuxtIslandResponse['components']> = {}
|
slots: {},
|
||||||
|
components: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (nuxtApp.isHydrating) {
|
if (nuxtApp.isHydrating) {
|
||||||
Object.assign(payloadSlots, toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots ?? {})
|
payloads.slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots ?? {}
|
||||||
Object.assign(payloadComponents, toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components ?? {})
|
payloads.components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components ?? {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ssrHTML = ref<string>('')
|
const ssrHTML = ref<string>('')
|
||||||
@ -125,7 +128,7 @@ export default defineComponent({
|
|||||||
let html = ssrHTML.value
|
let html = ssrHTML.value
|
||||||
|
|
||||||
if (import.meta.client && !canLoadClientComponent.value) {
|
if (import.meta.client && !canLoadClientComponent.value) {
|
||||||
for (const [key, value] of Object.entries(payloadComponents || {})) {
|
for (const [key, value] of Object.entries(payloads.components || {})) {
|
||||||
html = html.replace(new RegExp(` data-island-uid="${uid.value}" data-island-component="${key}"[^>]*>`), (full) => {
|
html = html.replace(new RegExp(` data-island-uid="${uid.value}" data-island-component="${key}"[^>]*>`), (full) => {
|
||||||
return full + value.html
|
return full + value.html
|
||||||
})
|
})
|
||||||
@ -134,7 +137,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
|
return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
|
||||||
if (!currentSlots.includes(slotName)) {
|
if (!currentSlots.includes(slotName)) {
|
||||||
return full + (payloadSlots[slotName]?.fallback || '')
|
return full + (payloads.slots[slotName]?.fallback || '')
|
||||||
}
|
}
|
||||||
return full
|
return full
|
||||||
})
|
})
|
||||||
@ -186,8 +189,8 @@ export default defineComponent({
|
|||||||
ssrHTML.value = res.html.replaceAll(DATA_ISLAND_UID_RE, `data-island-uid="${uid.value}"`)
|
ssrHTML.value = res.html.replaceAll(DATA_ISLAND_UID_RE, `data-island-uid="${uid.value}"`)
|
||||||
key.value++
|
key.value++
|
||||||
error.value = null
|
error.value = null
|
||||||
Object.assign(payloadSlots, res.slots || {})
|
payloads.slots = res.slots || {}
|
||||||
Object.assign(payloadComponents, res.components || {})
|
payloads.components = res.components || {}
|
||||||
|
|
||||||
if (selectiveClient && import.meta.client) {
|
if (selectiveClient && import.meta.client) {
|
||||||
if (canLoadClientComponent.value && res.components) {
|
if (canLoadClientComponent.value && res.components) {
|
||||||
@ -226,7 +229,7 @@ export default defineComponent({
|
|||||||
} else if (import.meta.server || !nuxtApp.isHydrating || !nuxtApp.payload.serverRendered) {
|
} else if (import.meta.server || !nuxtApp.isHydrating || !nuxtApp.payload.serverRendered) {
|
||||||
await fetchComponent()
|
await fetchComponent()
|
||||||
} else if (selectiveClient && canLoadClientComponent.value) {
|
} else if (selectiveClient && canLoadClientComponent.value) {
|
||||||
await loadComponents(props.source, payloadComponents)
|
await loadComponents(props.source, payloads.components)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (_ctx: any, _cache: any) => {
|
return (_ctx: any, _cache: any) => {
|
||||||
@ -250,12 +253,12 @@ export default defineComponent({
|
|||||||
teleports.push(createVNode(Teleport,
|
teleports.push(createVNode(Teleport,
|
||||||
// use different selectors for even and odd teleportKey to force trigger the 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}` },
|
{ to: import.meta.client ? `${isKeyOdd ? 'div' : ''}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` },
|
||||||
{ default: () => (payloadSlots[slot].props?.length ? payloadSlots[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 (import.meta.server) {
|
if (import.meta.server) {
|
||||||
for (const [id, info] of Object.entries(payloadComponents ?? {})) {
|
for (const [id, info] of Object.entries(payloads.components ?? {})) {
|
||||||
const { html } = info
|
const { html } = info
|
||||||
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
|
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
|
||||||
default: () => [createStaticVNode(html, 1)]
|
default: () => [createStaticVNode(html, 1)]
|
||||||
@ -263,7 +266,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectiveClient && import.meta.client && canLoadClientComponent.value) {
|
if (selectiveClient && import.meta.client && canLoadClientComponent.value) {
|
||||||
for (const [id, info] of Object.entries(payloadComponents ?? {})) {
|
for (const [id, info] of Object.entries(payloads.components ?? {})) {
|
||||||
const { props } = info
|
const { props } = info
|
||||||
const component = components!.get(id)!
|
const component = components!.get(id)!
|
||||||
// use different selectors for even and odd teleportKey to force trigger the teleport
|
// use different selectors for even and odd teleportKey to force trigger the teleport
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { beforeEach } from 'node:test'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { h, nextTick } from 'vue'
|
import { h, nextTick } from 'vue'
|
||||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||||
@ -22,6 +23,19 @@ vi.mock('vue', async (original) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const consoleError = vi.spyOn(console, 'error')
|
||||||
|
const consoleWarn = vi.spyOn(console, 'warn')
|
||||||
|
|
||||||
|
function expectNoConsoleIssue() {
|
||||||
|
expect(consoleError).not.toHaveBeenCalled()
|
||||||
|
expect(consoleWarn).not.toHaveBeenCalled()
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
consoleError.mockClear()
|
||||||
|
consoleWarn.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
describe('runtime server component', () => {
|
describe('runtime server component', () => {
|
||||||
it('expect no data-v- attrbutes #23051', () => {
|
it('expect no data-v- attrbutes #23051', () => {
|
||||||
// @ts-expect-error mock
|
// @ts-expect-error mock
|
||||||
@ -95,6 +109,96 @@ describe('runtime server component', () => {
|
|||||||
expect(fetch).toHaveBeenCalledTimes(2)
|
expect(fetch).toHaveBeenCalledTimes(2)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(component.html()).toBe('<div>2</div>')
|
expect(component.html()).toBe('<div>2</div>')
|
||||||
vi.mocked(fetch).mockRestore()
|
vi.mocked(fetch).mockReset()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('client components', () => {
|
||||||
|
|
||||||
|
it('expect swapping nuxt-client should not trigger errors #25289', async () => {
|
||||||
|
const mockPath = '/nuxt-client.js'
|
||||||
|
const componentId = 'Client-12345'
|
||||||
|
|
||||||
|
vi.doMock(mockPath, () => ({
|
||||||
|
default: {
|
||||||
|
name: 'ClientComponent',
|
||||||
|
setup() {
|
||||||
|
return () => h('div', 'client component')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const stubFetch = vi.fn(() => {
|
||||||
|
return {
|
||||||
|
id: '123',
|
||||||
|
html: `<div data-island-uid>hello<div data-island-uid data-island-component="${componentId}"></div></div>`,
|
||||||
|
state: {},
|
||||||
|
head: {
|
||||||
|
link: [],
|
||||||
|
style: []
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
[componentId]: {
|
||||||
|
html: '<div>fallback</div>',
|
||||||
|
props: {},
|
||||||
|
chunk: mockPath
|
||||||
|
}
|
||||||
|
},
|
||||||
|
json() {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', stubFetch)
|
||||||
|
|
||||||
|
const wrapper = await mountSuspended(NuxtIsland, {
|
||||||
|
props: {
|
||||||
|
name: 'NuxtClient',
|
||||||
|
props: {
|
||||||
|
force: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attachTo: 'body'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(fetch).toHaveBeenCalledOnce()
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchInlineSnapshot(`
|
||||||
|
"<div data-island-uid="3">hello<div data-island-uid="3" data-island-component="Client-12345">
|
||||||
|
<div>client component</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--teleport start-->
|
||||||
|
<!--teleport end-->"
|
||||||
|
`)
|
||||||
|
|
||||||
|
// @ts-expect-error mock
|
||||||
|
vi.mocked(fetch).mockImplementation(() => ({
|
||||||
|
id: '123',
|
||||||
|
html: `<div data-island-uid>hello<div><div>fallback</div></div></div>`,
|
||||||
|
state: {},
|
||||||
|
head: {
|
||||||
|
link: [],
|
||||||
|
style: []
|
||||||
|
},
|
||||||
|
components: {},
|
||||||
|
json() {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
await wrapper.vm.$.exposed!.refresh()
|
||||||
|
await nextTick()
|
||||||
|
expect(wrapper.html()).toMatchInlineSnapshot( `
|
||||||
|
"<div data-island-uid="3">hello<div>
|
||||||
|
<div>fallback</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`)
|
||||||
|
|
||||||
|
vi.mocked(fetch).mockReset()
|
||||||
|
expectNoConsoleIssue()
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user