From 00978f7d14e85b49d9d334ea92fa8c03733ce64c Mon Sep 17 00:00:00 2001
From: linzhe <40790268+linzhe141@users.noreply.github.com>
Date: Tue, 2 Sep 2025 17:08:15 +0800
Subject: [PATCH] fix(Teleport): hydrate disabled Teleport with undefined
target (#11235)
close #11230
---
.../runtime-core/__tests__/hydration.spec.ts | 24 ++++++++++++
.../runtime-core/src/components/Teleport.ts | 38 ++++++++++++++-----
2 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index 6828e61ec5..c0f25ab0f6 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -2357,6 +2357,30 @@ describe('SSR hydration', () => {
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
})
+ test('with disabled teleport + undefined target', async () => {
+ const container = document.createElement('div')
+ const isOpen = ref(false)
+ const App = {
+ setup() {
+ return { isOpen }
+ },
+ template: `
+
+
+ Menu is open...
+
+ `,
+ }
+ container.innerHTML = await renderToString(h(App))
+ const app = createSSRApp(App)
+ app.mount(container)
+ isOpen.value = true
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ `
Menu is open...
`,
+ )
+ })
+
test('escape css var name', () => {
const container = document.createElement('div')
container.innerHTML = ``
diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts
index c37356a786..9b32989f0b 100644
--- a/packages/runtime-core/src/components/Teleport.ts
+++ b/packages/runtime-core/src/components/Teleport.ts
@@ -406,29 +406,43 @@ function hydrateTeleport(
optimized: boolean,
) => Node | null,
): Node | null {
+ function hydrateDisabledTeleport(
+ node: Node,
+ vnode: VNode,
+ targetStart: Node | null,
+ targetAnchor: Node | null,
+ ) {
+ vnode.anchor = hydrateChildren(
+ nextSibling(node),
+ vnode,
+ parentNode(node)!,
+ parentComponent,
+ parentSuspense,
+ slotScopeIds,
+ optimized,
+ )
+ vnode.targetStart = targetStart
+ vnode.targetAnchor = targetAnchor
+ }
+
const target = (vnode.target = resolveTarget(
vnode.props,
querySelector,
))
+ const disabled = isTeleportDisabled(vnode.props)
if (target) {
- const disabled = isTeleportDisabled(vnode.props)
// if multiple teleports rendered to the same target element, we need to
// pick up from where the last teleport finished instead of the first node
const targetNode =
(target as TeleportTargetElement)._lpa || target.firstChild
if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
if (disabled) {
- vnode.anchor = hydrateChildren(
- nextSibling(node),
+ hydrateDisabledTeleport(
+ node,
vnode,
- parentNode(node)!,
- parentComponent,
- parentSuspense,
- slotScopeIds,
- optimized,
+ targetNode,
+ targetNode && nextSibling(targetNode),
)
- vnode.targetStart = targetNode
- vnode.targetAnchor = targetNode && nextSibling(targetNode)
} else {
vnode.anchor = nextSibling(node)
@@ -470,6 +484,10 @@ function hydrateTeleport(
}
}
updateCssVars(vnode, disabled)
+ } else if (disabled) {
+ if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+ hydrateDisabledTeleport(node, vnode, node, nextSibling(node))
+ }
}
return vnode.anchor && nextSibling(vnode.anchor as Node)
}
--
2.47.3