From: edison Date: Wed, 5 Nov 2025 03:55:57 +0000 (+0800) Subject: feat(runtime-vapor): add support for async components in VaporKeepAlive (#14040) X-Git-Tag: v3.6.0-alpha.3~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e4b147a24fb64a258f3372de299a2b8486bb8e21;p=thirdparty%2Fvuejs%2Fcore.git feat(runtime-vapor): add support for async components in VaporKeepAlive (#14040) --- diff --git a/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts b/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts index fa7f481707..7c0fb07ae5 100644 --- a/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts @@ -1,10 +1,12 @@ -import { nextTick, ref } from '@vue/runtime-dom' +import { nextTick, onActivated, ref } from '@vue/runtime-dom' import { type VaporComponent, createComponent } from '../src/component' import { defineVaporAsyncComponent } from '../src/apiDefineAsyncComponent' import { makeRender } from './_utils' import { + VaporKeepAlive, createIf, createTemplateRefSetter, + defineVaporComponent, renderEffect, template, } from '@vue/runtime-vapor' @@ -758,7 +760,102 @@ describe('api: defineAsyncComponent', () => { test.todo('suspense with error handling', async () => {}) - test.todo('with KeepAlive', async () => {}) + test('with KeepAlive', async () => { + const spy = vi.fn() + let resolve: (comp: VaporComponent) => void + + const Foo = defineVaporAsyncComponent( + () => + new Promise(r => { + resolve = r as any + }), + ) + + const Bar = defineVaporAsyncComponent(() => + Promise.resolve( + defineVaporComponent({ + setup() { + return template('Bar')() + }, + }), + ), + ) + + const toggle = ref(true) + const { html } = define({ + setup() { + return createComponent(VaporKeepAlive, null, { + default: () => + createIf( + () => toggle.value, + () => createComponent(Foo), + () => createComponent(Bar), + ), + }) + }, + }).render() + expect(html()).toBe('') + + await nextTick() + resolve!( + defineVaporComponent({ + setup() { + onActivated(() => { + spy() + }) + return template('Foo')() + }, + }), + ) + + await timeout() + expect(html()).toBe('Foo') + expect(spy).toBeCalledTimes(1) - test.todo('with KeepAlive + include', async () => {}) + toggle.value = false + await timeout() + expect(html()).toBe('Bar') + }) + + test('with KeepAlive + include', async () => { + const spy = vi.fn() + let resolve: (comp: VaporComponent) => void + + const Foo = defineVaporAsyncComponent( + () => + new Promise(r => { + resolve = r as any + }), + ) + + const { html } = define({ + setup() { + return createComponent( + VaporKeepAlive, + { include: () => 'Foo' }, + { + default: () => createComponent(Foo), + }, + ) + }, + }).render() + expect(html()).toBe('') + + await nextTick() + resolve!( + defineVaporComponent({ + name: 'Foo', + setup() { + onActivated(() => { + spy() + }) + return template('Foo')() + }, + }), + ) + + await timeout() + expect(html()).toBe('Foo') + expect(spy).toBeCalledTimes(1) + }) }) diff --git a/packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts b/packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts index 53d47651bb..09c4cbfc0c 100644 --- a/packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts +++ b/packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts @@ -22,6 +22,7 @@ import { createIf, createTemplateRefSetter, createVaporApp, + defineVaporAsyncComponent, defineVaporComponent, renderEffect, setText, @@ -30,6 +31,7 @@ import { } from '../../src' const define = makeRender() +const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n)) describe('VaporKeepAlive', () => { let one: VaporComponent @@ -1045,7 +1047,81 @@ describe('VaporKeepAlive', () => { }) }) - test.todo('should work with async component', async () => {}) + test('should work with async component', async () => { + let resolve: (comp: VaporComponent) => void + const AsyncComp = defineVaporAsyncComponent( + () => + new Promise(r => { + resolve = r as any + }), + ) + + const toggle = ref(true) + const instanceRef = ref(null) + const { html } = define({ + setup() { + const setRef = createTemplateRefSetter() + return createComponent( + VaporKeepAlive, + { include: () => 'Foo' }, + { + default: () => { + return createIf( + () => toggle.value, + () => { + const n0 = createComponent(AsyncComp) + setRef(n0, instanceRef) + return n0 + }, + ) + }, + }, + ) + }, + }).render() + + expect(html()).toBe(``) + + resolve!( + defineVaporComponent({ + name: 'Foo', + setup(_, { expose }) { + const count = ref(0) + expose({ + inc: () => { + count.value++ + }, + }) + + const n0 = template(`

`)() as any + const x0 = child(n0) as any + renderEffect(() => { + setText(x0, String(count.value)) + }) + return n0 + }, + }), + ) + + await timeout() + // resolved + expect(html()).toBe(`

0

`) + + // change state + toggle out + instanceRef.value.inc() + toggle.value = false + await nextTick() + expect(html()).toBe('') + + // toggle in, state should be maintained + toggle.value = true + await nextTick() + expect(html()).toBe('

1

') + + toggle.value = false + await nextTick() + expect(html()).toBe('') + }) test('handle error in async onActivated', async () => { const err = new Error('foo') @@ -1193,7 +1269,39 @@ describe('VaporKeepAlive', () => { }) describe('vdom interop', () => { - test('render vdom component', async () => { + test('should work', () => { + const VdomComp = { + setup() { + onBeforeMount(() => oneHooks.beforeMount()) + onMounted(() => oneHooks.mounted()) + onActivated(() => oneHooks.activated()) + onDeactivated(() => oneHooks.deactivated()) + onUnmounted(() => oneHooks.unmounted()) + return () => h('div', null, 'hi') + }, + } + + const App = defineVaporComponent({ + setup() { + return createComponent(VaporKeepAlive, null, { + default: () => { + return createComponent(VdomComp) + }, + }) + }, + }) + + const container = document.createElement('div') + document.body.appendChild(container) + const app = createVaporApp(App) + app.use(vaporInteropPlugin) + app.mount(container) + + expect(container.innerHTML).toBe(`
hi
`) + assertHookCalls(oneHooks, [1, 1, 1, 0, 0]) + }) + + test('with v-if', async () => { const VdomComp = { setup() { const msg = ref('vdom') diff --git a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts index 4cde9454f0..fcc706888a 100644 --- a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts +++ b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts @@ -5,6 +5,7 @@ import { createAsyncComponentContext, currentInstance, handleError, + isKeepAlive, markAsyncBoundary, performAsyncHydrate, useAsyncComponentState, @@ -28,6 +29,7 @@ import { import { invokeArrayFns } from '@vue/shared' import { type TransitionOptions, insert, remove } from './block' import { parentNode } from './dom/node' +import type { KeepAliveInstance } from './components/KeepAlive' import { setTransitionHooks } from './components/Transition' /*@ __NO_SIDE_EFFECTS__ */ @@ -122,7 +124,7 @@ export function defineVaporAsyncComponent( // already resolved let resolvedComp = getResolvedComp() if (resolvedComp) { - frag!.update(() => createInnerComp(resolvedComp!, instance)) + frag!.update(() => createInnerComp(resolvedComp!, instance, frag)) return frag } @@ -149,8 +151,6 @@ export function defineVaporAsyncComponent( load() .then(() => { loaded.value = true - // TODO parent is keep-alive, force update so the loaded component's - // name is taken into account }) .catch(err => { onError(err) @@ -193,6 +193,14 @@ function createInnerComp( appContext, ) + if (parent.parent && isKeepAlive(parent.parent)) { + // If there is a parent KeepAlive, let it handle the resolved async component + // This will process shapeFlag and cache the component + ;(parent.parent as KeepAliveInstance).cacheComponent(instance) + // cache the wrapper instance as well + ;(parent.parent as KeepAliveInstance).cacheComponent(parent) + } + // set transition hooks if ($transition) setTransitionHooks(instance, $transition) diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 0f6d32ae8a..628e6b61c7 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -157,6 +157,11 @@ export function remove(block: Block, parent?: ParentNode): void { if (block.anchor) remove(block.anchor, parent) if ((block as DynamicFragment).scope) { ;(block as DynamicFragment).scope!.stop() + const scopes = (block as DynamicFragment).keptAliveScopes + if (scopes) { + scopes.forEach(scope => scope.stop()) + scopes.clear() + } } } } diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index c2c2cd1f0c..e850f08932 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -86,7 +86,10 @@ import { } from './dom/hydration' import { _next, createElement } from './dom/node' import { type TeleportFragment, isVaporTeleport } from './components/Teleport' -import type { KeepAliveInstance } from './components/KeepAlive' +import { + type KeepAliveInstance, + findParentKeepAlive, +} from './components/KeepAlive' import { insertionAnchor, insertionParent, @@ -688,7 +691,7 @@ export function mountComponent( anchor?: Node | null | 0, ): void { if (instance.shapeFlag! & ShapeFlags.COMPONENT_KEPT_ALIVE) { - ;(instance.parent as KeepAliveInstance).activate(instance, parent, anchor) + findParentKeepAlive(instance)!.activate(instance, parent, anchor) return } @@ -723,7 +726,7 @@ export function unmountComponent( instance.parent.vapor && instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ) { - ;(instance.parent as KeepAliveInstance).deactivate(instance) + findParentKeepAlive(instance)!.deactivate(instance) return } diff --git a/packages/runtime-vapor/src/components/KeepAlive.ts b/packages/runtime-vapor/src/components/KeepAlive.ts index c6ff01000d..573fe613b9 100644 --- a/packages/runtime-vapor/src/components/KeepAlive.ts +++ b/packages/runtime-vapor/src/components/KeepAlive.ts @@ -1,8 +1,12 @@ import { + type GenericComponentInstance, type KeepAliveProps, + type VNode, currentInstance, devtoolsComponentAdded, getComponentName, + isAsyncWrapper, + isKeepAlive, matches, onBeforeUnmount, onMounted, @@ -22,7 +26,11 @@ import { import { defineVaporComponent } from '../apiDefineComponent' import { ShapeFlags, invokeArrayFns, isArray } from '@vue/shared' import { createElement } from '../dom/node' -import { type VaporFragment, isFragment } from '../fragment' +import { + type DynamicFragment, + type VaporFragment, + isFragment, +} from '../fragment' export interface KeepAliveInstance extends VaporComponentInstance { activate: ( @@ -31,14 +39,16 @@ export interface KeepAliveInstance extends VaporComponentInstance { anchor?: Node | null | 0, ) => void deactivate: (instance: VaporComponentInstance) => void - process: (block: Block) => void + cacheComponent: (instance: VaporComponentInstance) => void getCachedComponent: ( comp: VaporComponent, ) => VaporComponentInstance | VaporFragment | undefined getStorageContainer: () => ParentNode + processFragment: (fragment: DynamicFragment) => void + cacheFragment: (fragment: DynamicFragment) => void } -type CacheKey = VaporComponent +type CacheKey = VaporComponent | VNode['type'] type Cache = Map type Keys = Set @@ -66,22 +76,30 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ } function shouldCache(instance: VaporComponentInstance) { + // For unresolved async wrappers, skip caching + // Wait for resolution and re-process in createInnerComp + if (isAsyncWrapper(instance) && !instance.type.__asyncResolved) { + return false + } + const { include, exclude } = props - const name = getComponentName(instance.type) + const name = getComponentName( + isAsyncWrapper(instance) + ? instance.type.__asyncResolved! + : instance.type, + ) return !( (include && (!name || !matches(include, name))) || (exclude && name && matches(exclude, name)) ) } - function cacheBlock() { + function innerCacheBlock( + key: CacheKey, + instance: VaporComponentInstance | VaporFragment, + ) { const { max } = props - // TODO suspense - const block = keepAliveInstance.block! - const innerBlock = getInnerBlock(block)! - if (!innerBlock || !shouldCache(innerBlock)) return - const key = innerBlock.type if (cache.has(key)) { // make this key the freshest keys.delete(key) @@ -93,56 +111,116 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ pruneCacheEntry(keys.values().next().value!) } } - cache.set( - key, - (current = - isFragment(block) && isFragment(block.nodes) - ? // cache the fragment nodes for vdom interop - block.nodes - : innerBlock), - ) + + cache.set(key, instance) + current = instance + } + + function cacheBlock() { + // TODO suspense + const block = keepAliveInstance.block! + const innerBlock = getInnerBlock(block)! + if (!innerBlock || !shouldCache(innerBlock)) return + + let toCache: VaporComponentInstance | VaporFragment + let key: CacheKey + let frag: VaporFragment | undefined + if (isFragment(block) && (frag = findInteropFragment(block))) { + // vdom component: cache the fragment + toCache = frag + key = frag.vnode!.type + } else { + // vapor component: cache the instance + toCache = innerBlock + key = innerBlock.type + } + innerCacheBlock(key, toCache) } onMounted(cacheBlock) onUpdated(cacheBlock) onBeforeUnmount(() => { - cache.forEach(item => { - const cached = getInnerComponent(item)! - resetShapeFlag(cached) - cache.delete(cached.type) + cache.forEach((cached, key) => { + const instance = getInstanceFromCache(cached) + if (!instance) return + + resetCachedShapeFlag(cached) + cache.delete(key) + // current instance will be unmounted as part of keep-alive's unmount if (current) { - const innerComp = getInnerComponent(current)! - if (innerComp.type === cached.type) { - const instance = cached.vapor - ? cached - : // vdom interop - (cached as any).component + const currentKey = isVaporComponent(current) + ? current.type + : current.vnode!.type + if (currentKey === key) { + // call deactivated hook const da = instance.da da && queuePostFlushCb(da) return } } - remove(item, storageContainer) + + remove(cached, storageContainer) }) }) keepAliveInstance.getStorageContainer = () => storageContainer - keepAliveInstance.getCachedComponent = comp => cache.get(comp) - const processShapeFlag = (keepAliveInstance.process = block => { - const instance = getInnerComponent(block) - if (!instance) return + keepAliveInstance.getCachedComponent = comp => { + return cache.get(comp) + } + + keepAliveInstance.cacheComponent = (instance: VaporComponentInstance) => { + if (!shouldCache(instance)) return + instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + innerCacheBlock(instance.type, instance) + } + + keepAliveInstance.processFragment = (frag: DynamicFragment) => { + const innerBlock = getInnerBlock(frag.nodes) + if (!innerBlock) return - if (cache.has(instance.type)) { - instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE + const fragment = findInteropFragment(frag.nodes) + if (fragment) { + if (cache.has(fragment.vnode!.type)) { + fragment.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE + } + if (shouldCache(innerBlock)) { + fragment.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } + } else { + if (cache.has(innerBlock.type)) { + innerBlock.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE + } + if (shouldCache(innerBlock)) { + innerBlock.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } } + } + + keepAliveInstance.cacheFragment = (fragment: DynamicFragment) => { + const innerBlock = getInnerBlock(fragment.nodes) + if (!innerBlock || !shouldCache(innerBlock)) return - if (shouldCache(instance)) { - instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + // Determine what to cache based on fragment type + let toCache: VaporComponentInstance | VaporFragment + let key: CacheKey + + // find vdom interop fragment + const frag = findInteropFragment(fragment) + if (frag) { + frag.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + toCache = frag + key = frag.vnode!.type + } else { + innerBlock.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + toCache = innerBlock + key = innerBlock.type } - }) + + innerCacheBlock(key, toCache) + } keepAliveInstance.activate = (instance, parentNode, anchor) => { current = instance @@ -154,6 +232,16 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ deactivate(instance, storageContainer) } + function resetCachedShapeFlag( + cached: VaporComponentInstance | VaporFragment, + ) { + if (isVaporComponent(cached)) { + resetShapeFlag(cached) + } else { + resetShapeFlag(cached.vnode) + } + } + let children = slots.default() if (isArray(children) && children.length > 1) { if (__DEV__) { @@ -162,15 +250,18 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ return children } - // `children` could be either a `VaporComponentInstance` or a `DynamicFragment` - // (when using `v-if` or ``). For `DynamicFragment` children, - // the `shapeFlag` is processed in `DynamicFragment.update`. Here only need - // to process the `VaporComponentInstance` - if (isVaporComponent(children)) processShapeFlag(children) + // Process shapeFlag for vapor and vdom components + // DynamicFragment (v-if, ) is processed in DynamicFragment.update + if (isVaporComponent(children)) { + children.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } else if (isInteropFragment(children)) { + children.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } function pruneCache(filter: (name: string) => boolean) { - cache.forEach((instance, key) => { - instance = getInnerComponent(instance)! + cache.forEach((cached, key) => { + const instance = getInstanceFromCache(cached) + if (!instance) return const name = getComponentName(instance.type) if (name && !filter(name)) { pruneCacheEntry(key) @@ -180,7 +271,9 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ function pruneCacheEntry(key: CacheKey) { const cached = cache.get(key)! - resetShapeFlag(cached) + + resetCachedShapeFlag(cached) + // don't unmount if the instance is the current one if (cached !== current) { remove(cached) @@ -207,26 +300,34 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ function getInnerBlock(block: Block): VaporComponentInstance | undefined { if (isVaporComponent(block)) { return block - } - if (isVdomInteropFragment(block)) { + } else if (isInteropFragment(block)) { return block.vnode as any - } - if (isFragment(block)) { + } else if (isFragment(block)) { return getInnerBlock(block.nodes) } } -function getInnerComponent(block: Block): VaporComponentInstance | undefined { - if (isVaporComponent(block)) { +function isInteropFragment(block: Block): block is VaporFragment { + return !!(isFragment(block) && block.vnode) +} + +function findInteropFragment(block: Block): VaporFragment | undefined { + if (isInteropFragment(block)) { return block - } else if (isVdomInteropFragment(block)) { - // vdom interop - return block.vnode as any + } + if (isFragment(block)) { + return findInteropFragment(block.nodes) } } -function isVdomInteropFragment(block: Block): block is VaporFragment { - return !!(isFragment(block) && block.insert) +function getInstanceFromCache( + cached: VaporComponentInstance | VaporFragment, +): GenericComponentInstance { + if (isVaporComponent(cached)) { + return cached + } + // vdom interop + return cached.vnode!.component as GenericComponentInstance } export function activate( @@ -261,3 +362,16 @@ export function deactivate( devtoolsComponentAdded(instance) } } + +export function findParentKeepAlive( + instance: VaporComponentInstance, +): KeepAliveInstance | null { + let parent = instance as GenericComponentInstance | null + while (parent) { + if (isKeepAlive(parent)) { + return parent as KeepAliveInstance + } + parent = parent.parent + } + return null +} diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index 07f1243e4e..e30909ea06 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -73,6 +73,8 @@ export class DynamicFragment extends VaporFragment { current?: BlockFn fallback?: BlockFn anchorLabel?: string + inKeepAlive?: boolean + keptAliveScopes?: Map constructor(anchorLabel?: string) { super([]) @@ -96,11 +98,13 @@ export class DynamicFragment extends VaporFragment { const parent = isHydrating ? null : this.anchor.parentNode const transition = this.$transition const instance = currentInstance! - + this.inKeepAlive = isKeepAlive(instance) // teardown previous branch if (this.scope) { - if (isKeepAlive(instance)) { - ;(instance as KeepAliveInstance).process(this.nodes) + if (this.inKeepAlive) { + ;(instance as KeepAliveInstance).processFragment(this) + if (!this.keptAliveScopes) this.keptAliveScopes = new Map() + this.keptAliveScopes.set(this.current, this.scope) } else { this.scope.stop() } @@ -156,10 +160,22 @@ export class DynamicFragment extends VaporFragment { parent: ParentNode | null, ) { if (render) { - this.scope = new EffectScope() + // For KeepAlive, try to reuse the keepAlive scope for this key + const scope = + this.inKeepAlive && this.keptAliveScopes + ? this.keptAliveScopes.get(this.current) + : undefined + if (scope) { + this.scope = scope + this.keptAliveScopes!.delete(this.current!) + this.scope.resume() + } else { + this.scope = new EffectScope() + } + this.nodes = this.scope.run(render) || [] - if (isKeepAlive(instance)) { - ;(instance as KeepAliveInstance).process(this.nodes) + if (this.inKeepAlive) { + ;(instance as KeepAliveInstance).cacheFragment(this) } if (transition) { this.$transition = applyTransitionHooks(this.nodes, transition) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index a44c078e6a..c516f64c17 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -76,9 +76,9 @@ import { VaporFragment, isFragment, setFragmentFallback } from './fragment' import type { NodeRef } from './apiTemplateRef' import { setTransitionHooks as setVaporTransitionHooks } from './components/Transition' import { - type KeepAliveInstance, activate, deactivate, + findParentKeepAlive, } from './components/KeepAlive' export const interopKey: unique symbol = Symbol(`interop`) @@ -276,6 +276,7 @@ function createVDOMComponent( rawProps?: LooseRawProps | null, rawSlots?: LooseRawSlots | null, ): VaporFragment { + const parentInstance = currentInstance as VaporComponentInstance const frag = new VaporFragment([]) const vnode = (frag.vnode = createVNode( component, @@ -307,7 +308,6 @@ function createVDOMComponent( let rawRef: VNodeNormalizedRef | null = null let isMounted = false - const parentInstance = currentInstance as VaporComponentInstance const unmount = (parentNode?: ParentNode, transition?: TransitionHooks) => { // unset ref if (rawRef) vdomSetRef(rawRef, null, null, vnode, true) @@ -315,7 +315,7 @@ function createVDOMComponent( if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { vdomDeactivate( vnode, - (parentInstance as KeepAliveInstance).getStorageContainer(), + findParentKeepAlive(parentInstance)!.getStorageContainer(), internals, parentInstance as any, null, @@ -348,39 +348,38 @@ function createVDOMComponent( undefined, false, ) - return - } - - const prev = currentInstance - simpleSetCurrentInstance(parentInstance) - if (!isMounted) { - if (transition) setVNodeTransitionHooks(vnode, transition) - internals.mt( - vnode, - parentNode, - anchor, - parentInstance as any, - null, - undefined, - false, - ) - // set ref - if (rawRef) vdomSetRef(rawRef, null, null, vnode) - onScopeDispose(unmount, true) - isMounted = true } else { - // move - internals.m( - vnode, - parentNode, - anchor, - MoveType.REORDER, - parentInstance as any, - ) + const prev = currentInstance + simpleSetCurrentInstance(parentInstance) + if (!isMounted) { + if (transition) setVNodeTransitionHooks(vnode, transition) + internals.mt( + vnode, + parentNode, + anchor, + parentInstance as any, + null, + undefined, + false, + ) + // set ref + if (rawRef) vdomSetRef(rawRef, null, null, vnode) + onScopeDispose(unmount, true) + isMounted = true + } else { + // move + internals.m( + vnode, + parentNode, + anchor, + MoveType.REORDER, + parentInstance as any, + ) + } + simpleSetCurrentInstance(prev) } frag.nodes = vnode.el as any - simpleSetCurrentInstance(prev) } frag.remove = unmount