diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index f6ff8803c..be1a3e531 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -196,6 +196,10 @@ export interface ComponentInternalOptions { * indicates vapor component */ __vapor?: boolean + /** + * indicates keep-alive component + */ + __isKeepAlive?: boolean /** * @internal */ diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index 89b0e83d5..eaa10e359 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -456,7 +456,7 @@ function registerKeepAliveHook( let current = target.parent while (current && current.parent) { let parent = current.parent - if (isKeepAlive(parent.vapor ? (parent as any) : parent.vnode)) { + if (isKeepAlive(parent.vapor ? parent : parent.vnode)) { injectToKeepAliveRoot(wrappedHook, type, target, current) } current = current.parent diff --git a/packages/runtime-vapor/src/apiTemplateRef.ts b/packages/runtime-vapor/src/apiTemplateRef.ts index 06a2c63af..fe4aca75e 100644 --- a/packages/runtime-vapor/src/apiTemplateRef.ts +++ b/packages/runtime-vapor/src/apiTemplateRef.ts @@ -50,11 +50,7 @@ export function setRef( if (!instance || instance.isUnmounted) return const setupState: any = __DEV__ ? instance.setupState || {} : null - const refValue = isVaporComponent(el) - ? getExposed(el) || el - : el instanceof DynamicFragment - ? getExposed(el.nodes as VaporComponentInstance) || el.nodes - : el + const refValue = getRefValue(el) const refs = instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs @@ -147,3 +143,12 @@ export function setRef( } return ref } + +const getRefValue = (el: RefEl) => { + if (isVaporComponent(el)) { + return getExposed(el) || el + } else if (el instanceof DynamicFragment) { + return getRefValue(el.nodes as RefEl) + } + return el +} diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 4ad5e7f02..957ad1f99 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -152,9 +152,12 @@ export function createComponent( locateHydrationNode() } + // try to get the cached instance if inside keep-alive if (currentInstance && isKeepAlive(currentInstance)) { - const cache = (currentInstance as KeepAliveInstance).getCache(component) - if (cache) return cache + const cached = (currentInstance as KeepAliveInstance).getCachedInstance( + component, + ) + if (cached) return cached } // vdom interop enabled and component is not an explicit vapor component @@ -525,7 +528,7 @@ export function mountComponent( } if (instance.bm) invokeArrayFns(instance.bm) insert(instance.block, parentNode, anchor) - if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!)) + if (instance.m) queuePostFlushCb(instance.m!) if ( instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE && instance.a @@ -558,7 +561,7 @@ export function unmountComponent( instance.scope.stop() if (instance.um) { - queuePostFlushCb(() => invokeArrayFns(instance.um!)) + queuePostFlushCb(instance.um!) } instance.isUnmounted = true } diff --git a/packages/runtime-vapor/src/components/KeepAlive.ts b/packages/runtime-vapor/src/components/KeepAlive.ts index 127f0c94f..6321ba503 100644 --- a/packages/runtime-vapor/src/components/KeepAlive.ts +++ b/packages/runtime-vapor/src/components/KeepAlive.ts @@ -12,13 +12,7 @@ import { warn, watch, } from '@vue/runtime-dom' -import { - type Block, - DynamicFragment, - insert, - isFragment, - isValidBlock, -} from '../block' +import { type Block, insert, isFragment } from '../block' import { type ObjectVaporComponent, type VaporComponent, @@ -37,16 +31,17 @@ export interface KeepAliveInstance extends VaporComponentInstance { ) => void deactivate: (instance: VaporComponentInstance) => void process: (instance: VaporComponentInstance) => void - getCache: (comp: VaporComponent) => VaporComponentInstance | undefined + getCachedInstance: ( + comp: VaporComponent, + ) => VaporComponentInstance | undefined } -type CacheKey = PropertyKey | VaporComponent +type CacheKey = VaporComponent type Cache = Map type Keys = Set export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ name: 'VaporKeepAlive', - // @ts-expect-error __isKeepAlive: true, props: { include: [String, RegExp, Array], @@ -80,13 +75,10 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ function cacheBlock() { const { max } = props // TODO suspense - const currentBlock = keepAliveInstance.block! - if (!isValidBlock(currentBlock)) return + const innerBlock = getInnerBlock(keepAliveInstance.block!)! + if (!innerBlock || !shouldCache(innerBlock)) return - const block = getInnerBlock(currentBlock)! - if (!block || !shouldCache(block)) return - - const key = block.type + const key = innerBlock.type if (cache.has(key)) { // make this key the freshest keys.delete(key) @@ -98,7 +90,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ pruneCacheEntry(keys.values().next().value!) } } - cache.set(key, (current = block)) + cache.set(key, (current = innerBlock)) } onMounted(cacheBlock) @@ -118,9 +110,12 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ }) }) - keepAliveInstance.getCache = (comp: VaporComponent) => cache.get(comp) + keepAliveInstance.getCachedInstance = (comp: VaporComponent) => + cache.get(comp) - keepAliveInstance.process = (instance: VaporComponentInstance) => { + const process = (keepAliveInstance.process = ( + instance: VaporComponentInstance, + ) => { if (cache.has(instance.type)) { instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE } @@ -128,15 +123,15 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ if (shouldCache(instance)) { instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE } - } + }) keepAliveInstance.activate = ( instance: VaporComponentInstance, parentNode: ParentNode, anchor: Node, ) => { - const cachedBlock = (current = cache.get(instance.type)!) - insert((instance.block = cachedBlock.block), parentNode, anchor) + current = instance + insert(instance.block, parentNode, anchor) queuePostFlushCb(() => { instance.isDeactivated = false if (instance.a) invokeArrayFns(instance.a) @@ -167,12 +162,11 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ return children } - // wrap children in dynamic fragment - if (!isFragment(children)) { - const frag = new DynamicFragment() - frag.update(() => children) - children = frag - } + // `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)) process(children) function pruneCache(filter: (name: string) => boolean) { cache.forEach((instance, key) => { @@ -187,6 +181,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ const cached = cache.get(key) if (cached) { resetShapeFlag(cached) + // don't unmount if the instance is the current one if (cached !== current) { unmountComponent(cached) }