diff --git a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
index 6d3f6a9b8b6..f6d17cc2a90 100644
--- a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
+++ b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
@@ -4,6 +4,7 @@ import {
type ComponentPublicInstance,
KeepAlive,
type Ref,
+ Teleport,
type TestElement,
cloneVNode,
createApp,
@@ -22,6 +23,7 @@ import {
reactive,
ref,
render,
+ resolveDynamicComponent,
serializeInner,
shallowRef,
} from '@vue/runtime-test'
@@ -977,4 +979,59 @@ describe('KeepAlive', () => {
expect(mountedB).toHaveBeenCalledTimes(1)
expect(unmountedB).toHaveBeenCalledTimes(0)
})
+
+ // #11410
+ test('teleport in keepalive should be cached', async () => {
+ const activeComponent = shallowRef()
+ const App = {
+ name: 'App',
+ setup() {
+ const headerRef = ref()
+ const provided = reactive({ headerRef })
+ provide('App', provided)
+ return () => {
+ const render = [h('div', { ref: headerRef })]
+ if (activeComponent.value) {
+ render.push(
+ // @ts-expect-error
+ h(KeepAlive, null, [
+ resolveDynamicComponent(h(activeComponent.value)),
+ ]),
+ )
+ }
+ return render
+ }
+ },
+ }
+ const Comp1 = {
+ name: 'Comp1',
+ setup() {
+ const App = inject('App') as any
+ return () => h(Teleport, { to: App.headerRef }, ['xxx'])
+ },
+ }
+ const Comp2 = {
+ name: 'Comp2',
+ setup() {
+ return () => h('div', null, 'Comp2')
+ },
+ }
+ const root = nodeOps.createElement('div')
+ render(h(App), root)
+ activeComponent.value = Comp1
+ await nextTick()
+ expect(serializeInner(root)).toMatchInlineSnapshot(
+ `"
xxx
"`,
+ )
+ activeComponent.value = Comp2
+ await nextTick()
+ expect(serializeInner(root)).toMatchInlineSnapshot(
+ `"Comp2
"`,
+ )
+ activeComponent.value = Comp1
+ await nextTick()
+ expect(serializeInner(root)).toMatchInlineSnapshot(
+ `"xxx
"`,
+ )
+ })
})
diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts
index a2c8c2bf1a7..7dd42fbf16a 100644
--- a/packages/runtime-core/src/components/KeepAlive.ts
+++ b/packages/runtime-core/src/components/KeepAlive.ts
@@ -47,6 +47,7 @@ import { devtoolsComponentAdded } from '../devtools'
import { isAsyncWrapper } from '../apiAsyncComponent'
import { isSuspense } from './Suspense'
import { LifecycleHooks } from '../enums'
+import type { TeleportImpl } from './Teleport'
type MatchPattern = string | RegExp | (string | RegExp)[]
@@ -136,6 +137,14 @@ const KeepAliveImpl: ComponentOptions = {
) => {
const instance = vnode.component!
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
+ processPotentialTeleport(vnode, teleportVnode => {
+ ;(teleportVnode.type as typeof TeleportImpl).activate(
+ teleportVnode,
+ teleportVnode.target!,
+ null,
+ sharedContext.renderer,
+ )
+ })
// in case props have changed
patch(
instance.vnode,
@@ -169,8 +178,16 @@ const KeepAliveImpl: ComponentOptions = {
const instance = vnode.component!
invalidateMount(instance.m)
invalidateMount(instance.a)
-
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
+
+ processPotentialTeleport(vnode, teleportVnode => {
+ ;(teleportVnode.type as typeof TeleportImpl).deactivate(
+ teleportVnode,
+ storageContainer,
+ null,
+ sharedContext.renderer,
+ )
+ })
queuePostRenderEffect(() => {
if (instance.da) {
invokeArrayFns(instance.da)
@@ -456,3 +473,32 @@ function resetShapeFlag(vnode: VNode) {
function getInnerChild(vnode: VNode) {
return vnode.shapeFlag & ShapeFlags.SUSPENSE ? vnode.ssContent! : vnode
}
+
+function processPotentialTeleport(
+ vnode: VNode,
+ handle: (teleportVnode: VNode) => void,
+) {
+ if (vnode.shapeFlag & ShapeFlags.TELEPORT) {
+ handle && handle(vnode)
+ }
+ if (vnode.component) {
+ const subTree = vnode.component.subTree
+ if (subTree.shapeFlag & ShapeFlags.TELEPORT)
+ processPotentialTeleport(subTree, handle)
+ else if (isArray(subTree.children)) {
+ for (let i = 0; i < subTree.children.length; i++) {
+ const child = subTree.children[i]
+ if (child) {
+ processPotentialTeleport(child as VNode, handle)
+ }
+ }
+ }
+ } else if (isArray(vnode.children)) {
+ for (let i = 0; i < vnode.children.length; i++) {
+ const child = vnode.children[i]
+ if (child) {
+ processPotentialTeleport(child as VNode, handle)
+ }
+ }
+ }
+}
diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts
index 65437300cff..47737e6c143 100644
--- a/packages/runtime-core/src/components/Teleport.ts
+++ b/packages/runtime-core/src/components/Teleport.ts
@@ -292,7 +292,34 @@ export const TeleportImpl = {
}
}
},
-
+ activate: (
+ vnode: VNode,
+ container: RendererElement,
+ parentAnchor: RendererNode | null,
+ internals: RendererInternals,
+ ) => {
+ moveTeleport(
+ vnode,
+ container,
+ parentAnchor,
+ internals,
+ TeleportMoveTypes.TOGGLE,
+ )
+ },
+ deactivate: (
+ vnode: VNode,
+ container: RendererElement,
+ parentAnchor: RendererNode | null,
+ internals: RendererInternals,
+ ) => {
+ moveTeleport(
+ vnode,
+ container,
+ parentAnchor,
+ internals,
+ TeleportMoveTypes.TOGGLE,
+ )
+ },
move: moveTeleport,
hydrate: hydrateTeleport,
}