From: linzhe <40790268+linzhe141@users.noreply.github.com> Date: Tue, 2 Sep 2025 09:08:15 +0000 (+0800) Subject: fix(Teleport): hydrate disabled Teleport with undefined target (#11235) X-Git-Tag: v3.5.21~14 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=00978f7d14e85b49d9d334ea92fa8c03733ce64c;p=thirdparty%2Fvuejs%2Fcore.git fix(Teleport): hydrate disabled Teleport with undefined target (#11235) close #11230 --- 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) }