parent
84f0353511
commit
595263c0e9
@ -202,7 +202,7 @@ describe('SSR hydration', () => {
|
|||||||
const fn = jest.fn()
|
const fn = jest.fn()
|
||||||
const teleportContainer = document.createElement('div')
|
const teleportContainer = document.createElement('div')
|
||||||
teleportContainer.id = 'teleport'
|
teleportContainer.id = 'teleport'
|
||||||
teleportContainer.innerHTML = `<span>foo</span><span class="foo"></span><!---->`
|
teleportContainer.innerHTML = `<span>foo</span><span class="foo"></span><!--teleport anchor-->`
|
||||||
document.body.appendChild(teleportContainer)
|
document.body.appendChild(teleportContainer)
|
||||||
|
|
||||||
const { vnode, container } = mountWithHydration(
|
const { vnode, container } = mountWithHydration(
|
||||||
@ -233,7 +233,7 @@ describe('SSR hydration', () => {
|
|||||||
msg.value = 'bar'
|
msg.value = 'bar'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(teleportContainer.innerHTML).toBe(
|
expect(teleportContainer.innerHTML).toBe(
|
||||||
`<span>bar</span><span class="bar"></span><!---->`
|
`<span>bar</span><span class="bar"></span><!--teleport anchor-->`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ describe('SSR hydration', () => {
|
|||||||
|
|
||||||
const teleportHtml = ctx.teleports!['#teleport2']
|
const teleportHtml = ctx.teleports!['#teleport2']
|
||||||
expect(teleportHtml).toMatchInlineSnapshot(
|
expect(teleportHtml).toMatchInlineSnapshot(
|
||||||
`"<span>foo</span><span class=\\"foo\\"></span><!----><span>foo2</span><span class=\\"foo2\\"></span><!---->"`
|
`"<span>foo</span><span class=\\"foo\\"></span><!--teleport anchor--><span>foo2</span><span class=\\"foo2\\"></span><!--teleport anchor-->"`
|
||||||
)
|
)
|
||||||
|
|
||||||
teleportContainer.innerHTML = teleportHtml
|
teleportContainer.innerHTML = teleportHtml
|
||||||
@ -300,7 +300,7 @@ describe('SSR hydration', () => {
|
|||||||
msg.value = 'bar'
|
msg.value = 'bar'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(teleportContainer.innerHTML).toMatchInlineSnapshot(
|
expect(teleportContainer.innerHTML).toMatchInlineSnapshot(
|
||||||
`"<span>bar</span><span class=\\"bar\\"></span><!----><span>bar2</span><span class=\\"bar2\\"></span><!---->"`
|
`"<span>bar</span><span class=\\"bar\\"></span><!--teleport anchor--><span>bar2</span><span class=\\"bar2\\"></span><!--teleport anchor-->"`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ describe('SSR hydration', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const teleportHtml = ctx.teleports!['#teleport3']
|
const teleportHtml = ctx.teleports!['#teleport3']
|
||||||
expect(teleportHtml).toMatchInlineSnapshot(`"<!---->"`)
|
expect(teleportHtml).toMatchInlineSnapshot(`"<!--teleport anchor-->"`)
|
||||||
|
|
||||||
teleportContainer.innerHTML = teleportHtml
|
teleportContainer.innerHTML = teleportHtml
|
||||||
document.body.appendChild(teleportContainer)
|
document.body.appendChild(teleportContainer)
|
||||||
@ -369,7 +369,7 @@ describe('SSR hydration', () => {
|
|||||||
test('Teleport (as component root)', () => {
|
test('Teleport (as component root)', () => {
|
||||||
const teleportContainer = document.createElement('div')
|
const teleportContainer = document.createElement('div')
|
||||||
teleportContainer.id = 'teleport4'
|
teleportContainer.id = 'teleport4'
|
||||||
teleportContainer.innerHTML = `hello<!---->`
|
teleportContainer.innerHTML = `hello<!--teleport anchor-->`
|
||||||
document.body.appendChild(teleportContainer)
|
document.body.appendChild(teleportContainer)
|
||||||
|
|
||||||
const wrapper = {
|
const wrapper = {
|
||||||
@ -395,6 +395,38 @@ describe('SSR hydration', () => {
|
|||||||
expect(nextVNode.el).toBe(container.firstChild?.lastChild)
|
expect(nextVNode.el).toBe(container.firstChild?.lastChild)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Teleport (nested)', () => {
|
||||||
|
const teleportContainer = document.createElement('div')
|
||||||
|
teleportContainer.id = 'teleport5'
|
||||||
|
teleportContainer.innerHTML = `<div><!--teleport start--><!--teleport end--></div><!--teleport anchor--><div>child</div><!--teleport anchor-->`
|
||||||
|
document.body.appendChild(teleportContainer)
|
||||||
|
|
||||||
|
const { vnode, container } = mountWithHydration(
|
||||||
|
'<!--teleport start--><!--teleport end-->',
|
||||||
|
() =>
|
||||||
|
h(Teleport, { to: '#teleport5' }, [
|
||||||
|
h('div', [h(Teleport, { to: '#teleport5' }, [h('div', 'child')])])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(vnode.el).toBe(container.firstChild)
|
||||||
|
expect(vnode.anchor).toBe(container.lastChild)
|
||||||
|
|
||||||
|
const childDivVNode = (vnode as any).children[0]
|
||||||
|
const div = teleportContainer.firstChild
|
||||||
|
expect(childDivVNode.el).toBe(div)
|
||||||
|
expect(vnode.targetAnchor).toBe(div?.nextSibling)
|
||||||
|
|
||||||
|
const childTeleportVNode = childDivVNode.children[0]
|
||||||
|
expect(childTeleportVNode.el).toBe(div?.firstChild)
|
||||||
|
expect(childTeleportVNode.anchor).toBe(div?.lastChild)
|
||||||
|
|
||||||
|
expect(childTeleportVNode.targetAnchor).toBe(teleportContainer.lastChild)
|
||||||
|
expect(childTeleportVNode.children[0].el).toBe(
|
||||||
|
teleportContainer.lastChild?.previousSibling
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
// compile SSR + client render fn from the same template & hydrate
|
// compile SSR + client render fn from the same template & hydrate
|
||||||
test('full compiler integration', async () => {
|
test('full compiler integration', async () => {
|
||||||
const mounted: string[] = []
|
const mounted: string[] = []
|
||||||
|
@ -353,7 +353,26 @@ function hydrateTeleport(
|
|||||||
vnode.targetAnchor = targetNode
|
vnode.targetAnchor = targetNode
|
||||||
} else {
|
} else {
|
||||||
vnode.anchor = nextSibling(node)
|
vnode.anchor = nextSibling(node)
|
||||||
vnode.targetAnchor = hydrateChildren(
|
|
||||||
|
// lookahead until we find the target anchor
|
||||||
|
// we cannot rely on return value of hydrateChildren() because there
|
||||||
|
// could be nested teleports
|
||||||
|
let targetAnchor = targetNode
|
||||||
|
while (targetAnchor) {
|
||||||
|
targetAnchor = nextSibling(targetAnchor)
|
||||||
|
if (
|
||||||
|
targetAnchor &&
|
||||||
|
targetAnchor.nodeType === 8 &&
|
||||||
|
(targetAnchor as Comment).data === 'teleport anchor'
|
||||||
|
) {
|
||||||
|
vnode.targetAnchor = targetAnchor
|
||||||
|
;(target as TeleportTargetElement)._lpa =
|
||||||
|
vnode.targetAnchor && nextSibling(vnode.targetAnchor as Node)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hydrateChildren(
|
||||||
targetNode,
|
targetNode,
|
||||||
vnode,
|
vnode,
|
||||||
target,
|
target,
|
||||||
@ -363,8 +382,6 @@ function hydrateTeleport(
|
|||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
;(target as TeleportTargetElement)._lpa =
|
|
||||||
vnode.targetAnchor && nextSibling(vnode.targetAnchor as Node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vnode.anchor && nextSibling(vnode.anchor as Node)
|
return vnode.anchor && nextSibling(vnode.anchor as Node)
|
||||||
|
@ -31,7 +31,9 @@ describe('ssrRenderTeleport', () => {
|
|||||||
ctx
|
ctx
|
||||||
)
|
)
|
||||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||||
expect(ctx.teleports!['#target']).toBe(`<div>content</div><!---->`)
|
expect(ctx.teleports!['#target']).toBe(
|
||||||
|
`<div>content</div><!--teleport anchor-->`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('teleport rendering (compiled + disabled)', async () => {
|
test('teleport rendering (compiled + disabled)', async () => {
|
||||||
@ -58,7 +60,7 @@ describe('ssrRenderTeleport', () => {
|
|||||||
expect(html).toBe(
|
expect(html).toBe(
|
||||||
'<!--teleport start--><div>content</div><!--teleport end-->'
|
'<!--teleport start--><div>content</div><!--teleport end-->'
|
||||||
)
|
)
|
||||||
expect(ctx.teleports!['#target']).toBe(`<!---->`)
|
expect(ctx.teleports!['#target']).toBe(`<!--teleport anchor-->`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('teleport rendering (vnode)', async () => {
|
test('teleport rendering (vnode)', async () => {
|
||||||
@ -74,7 +76,9 @@ describe('ssrRenderTeleport', () => {
|
|||||||
ctx
|
ctx
|
||||||
)
|
)
|
||||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||||
expect(ctx.teleports!['#target']).toBe('<span>hello</span><!---->')
|
expect(ctx.teleports!['#target']).toBe(
|
||||||
|
'<span>hello</span><!--teleport anchor-->'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('teleport rendering (vnode + disabled)', async () => {
|
test('teleport rendering (vnode + disabled)', async () => {
|
||||||
@ -93,7 +97,7 @@ describe('ssrRenderTeleport', () => {
|
|||||||
expect(html).toBe(
|
expect(html).toBe(
|
||||||
'<!--teleport start--><span>hello</span><!--teleport end-->'
|
'<!--teleport start--><span>hello</span><!--teleport end-->'
|
||||||
)
|
)
|
||||||
expect(ctx.teleports!['#target']).toBe(`<!---->`)
|
expect(ctx.teleports!['#target']).toBe(`<!--teleport anchor-->`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('multiple teleports with same target', async () => {
|
test('multiple teleports with same target', async () => {
|
||||||
@ -115,7 +119,7 @@ describe('ssrRenderTeleport', () => {
|
|||||||
'<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>'
|
'<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>'
|
||||||
)
|
)
|
||||||
expect(ctx.teleports!['#target']).toBe(
|
expect(ctx.teleports!['#target']).toBe(
|
||||||
'<span>hello</span><!---->world<!---->'
|
'<span>hello</span><!--teleport anchor-->world<!--teleport anchor-->'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -133,7 +137,9 @@ describe('ssrRenderTeleport', () => {
|
|||||||
ctx
|
ctx
|
||||||
)
|
)
|
||||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||||
expect(ctx.teleports!['#target']).toBe(`<div>content</div><!---->`)
|
expect(ctx.teleports!['#target']).toBe(
|
||||||
|
`<div>content</div><!--teleport anchor-->`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('teleport inside async component (stream)', async () => {
|
test('teleport inside async component (stream)', async () => {
|
||||||
@ -166,6 +172,8 @@ describe('ssrRenderTeleport', () => {
|
|||||||
)
|
)
|
||||||
await p
|
await p
|
||||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||||
expect(ctx.teleports!['#target']).toBe(`<div>content</div><!---->`)
|
expect(ctx.teleports!['#target']).toBe(
|
||||||
|
`<div>content</div><!--teleport anchor-->`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -10,28 +10,28 @@ export function ssrRenderTeleport(
|
|||||||
) {
|
) {
|
||||||
parentPush('<!--teleport start-->')
|
parentPush('<!--teleport start-->')
|
||||||
|
|
||||||
let teleportContent: SSRBufferItem
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
contentRenderFn(parentPush)
|
|
||||||
teleportContent = `<!---->`
|
|
||||||
} else {
|
|
||||||
const { getBuffer, push } = createBuffer()
|
|
||||||
contentRenderFn(push)
|
|
||||||
push(`<!---->`) // teleport end anchor
|
|
||||||
teleportContent = getBuffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = parentComponent.appContext.provides[
|
const context = parentComponent.appContext.provides[
|
||||||
ssrContextKey as any
|
ssrContextKey as any
|
||||||
] as SSRContext
|
] as SSRContext
|
||||||
const teleportBuffers =
|
const teleportBuffers =
|
||||||
context.__teleportBuffers || (context.__teleportBuffers = {})
|
context.__teleportBuffers || (context.__teleportBuffers = {})
|
||||||
if (teleportBuffers[target]) {
|
const targetBuffer = teleportBuffers[target] || (teleportBuffers[target] = [])
|
||||||
teleportBuffers[target].push(teleportContent)
|
// record current index of the target buffer to handle nested teleports
|
||||||
|
// since the parent needs to be rendered before the child
|
||||||
|
const bufferIndex = targetBuffer.length
|
||||||
|
|
||||||
|
let teleportContent: SSRBufferItem
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
contentRenderFn(parentPush)
|
||||||
|
teleportContent = `<!--teleport anchor-->`
|
||||||
} else {
|
} else {
|
||||||
teleportBuffers[target] = [teleportContent]
|
const { getBuffer, push } = createBuffer()
|
||||||
|
contentRenderFn(push)
|
||||||
|
push(`<!--teleport anchor-->`)
|
||||||
|
teleportContent = getBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetBuffer.splice(bufferIndex, 0, teleportContent)
|
||||||
parentPush('<!--teleport end-->')
|
parentPush('<!--teleport end-->')
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user