warn,
watch,
} from '@vue/runtime-dom'
-import { type Block, insert, isFragment } from '../block'
+import {
+ type Block,
+ type VaporFragment,
+ insert,
+ isFragment,
+ remove,
+} from '../block'
import {
type ObjectVaporComponent,
type VaporComponent,
type VaporComponentInstance,
isVaporComponent,
- unmountComponent,
} from '../component'
import { defineVaporComponent } from '../apiDefineComponent'
import { ShapeFlags, invokeArrayFns, isArray } from '@vue/shared'
export interface KeepAliveInstance extends VaporComponentInstance {
activate: (
- instance: VaporComponentInstance,
+ block: VaporComponentInstance | VaporFragment,
parentNode: ParentNode,
- anchor: Node,
+ anchor?: Node | null | 0,
) => void
- deactivate: (instance: VaporComponentInstance) => void
- process: (instance: VaporComponentInstance) => void
- getCachedInstance: (
+ deactivate: (block: VaporComponentInstance | VaporFragment) => void
+ process: (block: Block) => void
+ getCachedComponent: (
comp: VaporComponent,
- ) => VaporComponentInstance | undefined
+ ) => VaporComponentInstance | VaporFragment | undefined
}
type CacheKey = VaporComponent
-type Cache = Map<CacheKey, VaporComponentInstance>
+type Cache = Map<CacheKey, VaporComponentInstance | VaporFragment>
type Keys = Set<CacheKey>
export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
const cache: Cache = new Map()
const keys: Keys = new Set()
const storageContainer = createElement('div')
- let current: VaporComponentInstance | undefined
+ let current: VaporComponentInstance | VaporFragment | undefined
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
;(keepAliveInstance as any).__v_cache = cache
function cacheBlock() {
const { max } = props
// TODO suspense
- const innerBlock = getInnerBlock(keepAliveInstance.block!)!
+ const block = keepAliveInstance.block!
+ const innerBlock = getInnerBlock(block)!
if (!innerBlock || !shouldCache(innerBlock)) return
const key = innerBlock.type
pruneCacheEntry(keys.values().next().value!)
}
}
- cache.set(key, (current = innerBlock))
+ cache.set(
+ key,
+ (current =
+ isFragment(block) && isFragment(block.nodes)
+ ? // cache the fragment nodes for vdom interop
+ block.nodes
+ : innerBlock),
+ )
}
onMounted(cacheBlock)
onUpdated(cacheBlock)
onBeforeUnmount(() => {
- cache.forEach(cached => {
+ cache.forEach(item => {
+ const cached = getInnerComponent(item)!
resetShapeFlag(cached)
cache.delete(cached.type)
// current instance will be unmounted as part of keep-alive's unmount
- if (current && current.type === cached.type) {
- const da = cached.da
- da && queuePostFlushCb(da)
- return
+ if (current) {
+ const innerComp = getInnerComponent(current)!
+ if (innerComp.type === cached.type) {
+ const da = cached.da
+ da && queuePostFlushCb(da)
+ return
+ }
}
- unmountComponent(cached, storageContainer)
+ remove(cached, storageContainer)
})
})
- keepAliveInstance.getCachedInstance = (comp: VaporComponent) =>
- cache.get(comp)
+ keepAliveInstance.getCachedComponent = comp => cache.get(comp)
+
+ const process = (keepAliveInstance.process = block => {
+ const instance = getInnerComponent(block)
+ if (!instance) return
- const process = (keepAliveInstance.process = (
- instance: VaporComponentInstance,
- ) => {
if (cache.has(instance.type)) {
instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
}
}
})
- keepAliveInstance.activate = (
- instance: VaporComponentInstance,
- parentNode: ParentNode,
- anchor: Node,
- ) => {
- current = instance
- insert(instance.block, parentNode, anchor)
+ keepAliveInstance.activate = (block, parentNode, anchor) => {
+ current = block
+ let instance
+ if (isVaporComponent(block)) {
+ instance = block
+ insert(block.block, parentNode, anchor)
+ } else {
+ // vdom interop
+ const comp = block.nodes as any
+ insert(comp.el, parentNode, anchor)
+ instance = comp.component
+ }
+
queuePostFlushCb(() => {
instance.isDeactivated = false
if (instance.a) invokeArrayFns(instance.a)
}
}
- keepAliveInstance.deactivate = (instance: VaporComponentInstance) => {
- insert(instance.block, storageContainer)
+ keepAliveInstance.deactivate = block => {
+ let instance
+ if (isVaporComponent(block)) {
+ instance = block
+ insert(block.block, storageContainer)
+ } else {
+ // vdom interop
+ const comp = block.nodes as any
+ insert(comp.el, storageContainer)
+ instance = comp.component
+ }
+
queuePostFlushCb(() => {
if (instance.da) invokeArrayFns(instance.da)
instance.isDeactivated = true
function pruneCache(filter: (name: string) => boolean) {
cache.forEach((instance, key) => {
+ instance = getInnerComponent(instance)!
const name = getComponentName(instance.type)
if (name && !filter(name)) {
pruneCacheEntry(key)
resetShapeFlag(cached)
// don't unmount if the instance is the current one
if (cached !== current) {
- unmountComponent(cached)
+ remove(cached)
}
}
cache.delete(key)
if (isVaporComponent(block)) {
return block
}
+ if (isVdomInteropFragment(block)) {
+ return block.nodes as any
+ }
if (isFragment(block)) {
return getInnerBlock(block.nodes)
}
}
+
+function getInnerComponent(block: Block): VaporComponentInstance | undefined {
+ if (isVaporComponent(block)) {
+ return block
+ } else if (isVdomInteropFragment(block)) {
+ // vdom interop
+ return block.nodes as any
+ }
+}
+
+function isVdomInteropFragment(block: Block): block is VaporFragment {
+ return !!(isFragment(block) && block.insert)
+}
unmountComponent,
} from './component'
import { type Block, VaporFragment, insert, remove } from './block'
-import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
+import { EMPTY_OBJ, ShapeFlags, extend, isFunction } from '@vue/shared'
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
import type { RawSlots, VaporSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
import { createTextNode } from './dom/node'
import { optimizePropertyLookup } from './dom/prop'
+import type { KeepAliveInstance } from './components/KeepAlive'
// mounting vapor components and slots in vdom
const vaporInteropImpl: Omit<
let isMounted = false
const parentInstance = currentInstance as VaporComponentInstance
const unmount = (parentNode?: ParentNode) => {
+ if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
+ ;(parentInstance as KeepAliveInstance).deactivate(frag)
+ return
+ }
internals.umt(vnode.component!, null, !!parentNode)
}
frag.insert = (parentNode, anchor) => {
+ if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
+ ;(parentInstance as KeepAliveInstance).activate(frag, parentNode, anchor)
+ return
+ }
if (!isMounted) {
internals.mt(
vnode,
}
frag.remove = unmount
+ frag.nodes = vnode as any
return frag
}