|
|
@@ -54,229 +54,234 @@ type CacheKey = VaporComponent | VNode['type']
|
|
|
type Cache = Map<CacheKey, VaporComponentInstance | VaporFragment>
|
|
|
type Keys = Set<CacheKey>
|
|
|
|
|
|
-export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
|
|
|
- name: 'VaporKeepAlive',
|
|
|
- __isKeepAlive: true,
|
|
|
- props: {
|
|
|
- include: [String, RegExp, Array],
|
|
|
- exclude: [String, RegExp, Array],
|
|
|
- max: [String, Number],
|
|
|
- },
|
|
|
- setup(props: KeepAliveProps, { slots }) {
|
|
|
- if (!slots.default) {
|
|
|
- return undefined
|
|
|
- }
|
|
|
-
|
|
|
- const keepAliveInstance = currentInstance! as KeepAliveInstance
|
|
|
- const cache: Cache = new Map()
|
|
|
- const keys: Keys = new Set()
|
|
|
- const storageContainer = createElement('div')
|
|
|
- const keptAliveScopes = new Map<any, EffectScope>()
|
|
|
- let current: VaporComponentInstance | VaporFragment | undefined
|
|
|
+export const VaporKeepAliveImpl: ObjectVaporComponent =
|
|
|
+ /*@__PURE__*/ defineVaporComponent({
|
|
|
+ name: 'VaporKeepAlive',
|
|
|
+ __isKeepAlive: true,
|
|
|
+ props: {
|
|
|
+ include: [String, RegExp, Array],
|
|
|
+ exclude: [String, RegExp, Array],
|
|
|
+ max: [String, Number],
|
|
|
+ },
|
|
|
+ setup(props: KeepAliveProps, { slots }) {
|
|
|
+ if (!slots.default) {
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
|
|
|
- if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
|
|
- ;(keepAliveInstance as any).__v_cache = cache
|
|
|
- }
|
|
|
+ const keepAliveInstance = currentInstance! as KeepAliveInstance
|
|
|
+ const cache: Cache = new Map()
|
|
|
+ const keys: Keys = new Set()
|
|
|
+ const storageContainer = createElement('div')
|
|
|
+ const keptAliveScopes = new Map<any, EffectScope>()
|
|
|
+ let current: VaporComponentInstance | VaporFragment | undefined
|
|
|
|
|
|
- keepAliveInstance.getStorageContainer = () => storageContainer
|
|
|
+ if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
|
|
+ ;(keepAliveInstance as any).__v_cache = cache
|
|
|
+ }
|
|
|
|
|
|
- keepAliveInstance.getCachedComponent = comp => cache.get(comp)
|
|
|
+ keepAliveInstance.getStorageContainer = () => storageContainer
|
|
|
|
|
|
- keepAliveInstance.activate = (instance, parentNode, anchor) => {
|
|
|
- current = instance
|
|
|
- activate(instance, parentNode, anchor)
|
|
|
- }
|
|
|
+ keepAliveInstance.getCachedComponent = comp => cache.get(comp)
|
|
|
|
|
|
- keepAliveInstance.deactivate = instance => {
|
|
|
- current = undefined
|
|
|
- deactivate(instance, storageContainer)
|
|
|
- }
|
|
|
+ keepAliveInstance.activate = (instance, parentNode, anchor) => {
|
|
|
+ current = instance
|
|
|
+ activate(instance, parentNode, anchor)
|
|
|
+ }
|
|
|
|
|
|
- const innerCacheBlock = (
|
|
|
- key: CacheKey,
|
|
|
- instance: VaporComponentInstance | VaporFragment,
|
|
|
- ) => {
|
|
|
- const { max } = props
|
|
|
+ keepAliveInstance.deactivate = instance => {
|
|
|
+ current = undefined
|
|
|
+ deactivate(instance, storageContainer)
|
|
|
+ }
|
|
|
|
|
|
- if (cache.has(key)) {
|
|
|
- // make this key the freshest
|
|
|
- keys.delete(key)
|
|
|
- keys.add(key)
|
|
|
- } else {
|
|
|
- keys.add(key)
|
|
|
- // prune oldest entry
|
|
|
- if (max && keys.size > parseInt(max as string, 10)) {
|
|
|
- pruneCacheEntry(keys.values().next().value!)
|
|
|
+ const innerCacheBlock = (
|
|
|
+ key: CacheKey,
|
|
|
+ instance: VaporComponentInstance | VaporFragment,
|
|
|
+ ) => {
|
|
|
+ const { max } = props
|
|
|
+
|
|
|
+ if (cache.has(key)) {
|
|
|
+ // make this key the freshest
|
|
|
+ keys.delete(key)
|
|
|
+ keys.add(key)
|
|
|
+ } else {
|
|
|
+ keys.add(key)
|
|
|
+ // prune oldest entry
|
|
|
+ if (max && keys.size > parseInt(max as string, 10)) {
|
|
|
+ pruneCacheEntry(keys.values().next().value!)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- cache.set(key, instance)
|
|
|
- current = instance
|
|
|
- }
|
|
|
+ cache.set(key, instance)
|
|
|
+ current = instance
|
|
|
+ }
|
|
|
|
|
|
- const cacheBlock = () => {
|
|
|
- // TODO suspense
|
|
|
- const block = keepAliveInstance.block!
|
|
|
- const [innerBlock, interop] = getInnerBlock(block)
|
|
|
- if (!innerBlock || !shouldCache(innerBlock, props, interop)) return
|
|
|
- innerCacheBlock(
|
|
|
- interop ? innerBlock.vnode!.type : innerBlock.type,
|
|
|
- innerBlock,
|
|
|
- )
|
|
|
- }
|
|
|
+ const cacheBlock = () => {
|
|
|
+ // TODO suspense
|
|
|
+ const block = keepAliveInstance.block!
|
|
|
+ const [innerBlock, interop] = getInnerBlock(block)
|
|
|
+ if (!innerBlock || !shouldCache(innerBlock, props, interop)) return
|
|
|
+ innerCacheBlock(
|
|
|
+ interop ? innerBlock.vnode!.type : innerBlock.type,
|
|
|
+ innerBlock,
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
- const processFragment = (frag: DynamicFragment) => {
|
|
|
- const [innerBlock, interop] = getInnerBlock(frag.nodes)
|
|
|
- if (!innerBlock || !shouldCache(innerBlock!, props, interop)) return false
|
|
|
+ const processFragment = (frag: DynamicFragment) => {
|
|
|
+ const [innerBlock, interop] = getInnerBlock(frag.nodes)
|
|
|
+ if (!innerBlock || !shouldCache(innerBlock!, props, interop))
|
|
|
+ return false
|
|
|
|
|
|
- if (interop) {
|
|
|
- if (cache.has(innerBlock.vnode!.type)) {
|
|
|
- innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
|
|
|
- }
|
|
|
- innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
- } else {
|
|
|
- if (cache.has(innerBlock!.type)) {
|
|
|
- innerBlock!.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
|
|
|
+ if (interop) {
|
|
|
+ if (cache.has(innerBlock.vnode!.type)) {
|
|
|
+ innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
|
|
|
+ }
|
|
|
+ innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ } else {
|
|
|
+ if (cache.has(innerBlock!.type)) {
|
|
|
+ innerBlock!.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
|
|
|
+ }
|
|
|
+ innerBlock!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
}
|
|
|
- innerBlock!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ return true
|
|
|
}
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
- const cacheFragment = (fragment: DynamicFragment) => {
|
|
|
- const [innerBlock, interop] = getInnerBlock(fragment.nodes)
|
|
|
- if (!innerBlock || !shouldCache(innerBlock, props, interop)) return
|
|
|
-
|
|
|
- let key: CacheKey
|
|
|
- if (interop) {
|
|
|
- innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
- key = innerBlock.vnode!.type
|
|
|
- } else {
|
|
|
- innerBlock.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
- key = innerBlock.type
|
|
|
- }
|
|
|
- innerCacheBlock(key, innerBlock)
|
|
|
- }
|
|
|
|
|
|
- const pruneCache = (filter: (name: string) => boolean) => {
|
|
|
- cache.forEach((cached, key) => {
|
|
|
- const instance = getInstanceFromCache(cached)
|
|
|
- if (!instance) return
|
|
|
- const name = getComponentName(instance.type)
|
|
|
- if (name && !filter(name)) {
|
|
|
- pruneCacheEntry(key)
|
|
|
+ const cacheFragment = (fragment: DynamicFragment) => {
|
|
|
+ const [innerBlock, interop] = getInnerBlock(fragment.nodes)
|
|
|
+ if (!innerBlock || !shouldCache(innerBlock, props, interop)) return
|
|
|
+
|
|
|
+ let key: CacheKey
|
|
|
+ if (interop) {
|
|
|
+ innerBlock.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ key = innerBlock.vnode!.type
|
|
|
+ } else {
|
|
|
+ innerBlock.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ key = innerBlock.type
|
|
|
}
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- const pruneCacheEntry = (key: CacheKey) => {
|
|
|
- const cached = cache.get(key)!
|
|
|
-
|
|
|
- resetCachedShapeFlag(cached)
|
|
|
+ innerCacheBlock(key, innerBlock)
|
|
|
+ }
|
|
|
|
|
|
- // don't unmount if the instance is the current one
|
|
|
- if (cached !== current) {
|
|
|
- remove(cached)
|
|
|
+ const pruneCache = (filter: (name: string) => boolean) => {
|
|
|
+ cache.forEach((cached, key) => {
|
|
|
+ const instance = getInstanceFromCache(cached)
|
|
|
+ if (!instance) return
|
|
|
+ const name = getComponentName(instance.type)
|
|
|
+ if (name && !filter(name)) {
|
|
|
+ pruneCacheEntry(key)
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
- cache.delete(key)
|
|
|
- keys.delete(key)
|
|
|
- }
|
|
|
|
|
|
- // prune cache on include/exclude prop change
|
|
|
- watch(
|
|
|
- () => [props.include, props.exclude],
|
|
|
- ([include, exclude]) => {
|
|
|
- include && pruneCache(name => matches(include, name))
|
|
|
- exclude && pruneCache(name => !matches(exclude, name))
|
|
|
- },
|
|
|
- // prune post-render after `current` has been updated
|
|
|
- { flush: 'post', deep: true },
|
|
|
- )
|
|
|
-
|
|
|
- onMounted(cacheBlock)
|
|
|
- onUpdated(cacheBlock)
|
|
|
- onBeforeUnmount(() => {
|
|
|
- cache.forEach((cached, key) => {
|
|
|
- const instance = getInstanceFromCache(cached)
|
|
|
- if (!instance) return
|
|
|
+ const pruneCacheEntry = (key: CacheKey) => {
|
|
|
+ const cached = cache.get(key)!
|
|
|
|
|
|
resetCachedShapeFlag(cached)
|
|
|
+
|
|
|
+ // don't unmount if the instance is the current one
|
|
|
+ if (cached !== current) {
|
|
|
+ remove(cached)
|
|
|
+ }
|
|
|
cache.delete(key)
|
|
|
+ keys.delete(key)
|
|
|
+ }
|
|
|
+
|
|
|
+ // prune cache on include/exclude prop change
|
|
|
+ watch(
|
|
|
+ () => [props.include, props.exclude],
|
|
|
+ ([include, exclude]) => {
|
|
|
+ include && pruneCache(name => matches(include, name))
|
|
|
+ exclude && pruneCache(name => !matches(exclude, name))
|
|
|
+ },
|
|
|
+ // prune post-render after `current` has been updated
|
|
|
+ { flush: 'post', deep: true },
|
|
|
+ )
|
|
|
|
|
|
- // current instance will be unmounted as part of keep-alive's unmount
|
|
|
- if (current) {
|
|
|
- const currentKey = isVaporComponent(current)
|
|
|
- ? current.type
|
|
|
- : current.vnode!.type
|
|
|
- if (currentKey === key) {
|
|
|
- // call deactivated hook
|
|
|
- const da = instance.da
|
|
|
- da && queuePostFlushCb(da)
|
|
|
- return
|
|
|
+ onMounted(cacheBlock)
|
|
|
+ onUpdated(cacheBlock)
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ 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 currentKey = isVaporComponent(current)
|
|
|
+ ? current.type
|
|
|
+ : current.vnode!.type
|
|
|
+ if (currentKey === key) {
|
|
|
+ // call deactivated hook
|
|
|
+ const da = instance.da
|
|
|
+ da && queuePostFlushCb(da)
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- remove(cached, storageContainer)
|
|
|
+ remove(cached, storageContainer)
|
|
|
+ })
|
|
|
+ keptAliveScopes.forEach(scope => scope.stop())
|
|
|
+ keptAliveScopes.clear()
|
|
|
})
|
|
|
- keptAliveScopes.forEach(scope => scope.stop())
|
|
|
- keptAliveScopes.clear()
|
|
|
- })
|
|
|
-
|
|
|
- let children = slots.default()
|
|
|
- if (isArray(children)) {
|
|
|
- children = children.filter(child => !(child instanceof Comment))
|
|
|
- if (children.length > 1) {
|
|
|
- if (__DEV__) {
|
|
|
- warn(`KeepAlive should contain exactly one component child.`)
|
|
|
+
|
|
|
+ let children = slots.default()
|
|
|
+ if (isArray(children)) {
|
|
|
+ children = children.filter(child => !(child instanceof Comment))
|
|
|
+ if (children.length > 1) {
|
|
|
+ if (__DEV__) {
|
|
|
+ warn(`KeepAlive should contain exactly one component child.`)
|
|
|
+ }
|
|
|
+ return children
|
|
|
}
|
|
|
- return children
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // inject hooks to DynamicFragment to cache components during updates
|
|
|
- const injectKeepAliveHooks = (frag: DynamicFragment) => {
|
|
|
- ;(frag.onBeforeTeardown || (frag.onBeforeTeardown = [])).push(
|
|
|
- (oldKey, nodes, scope) => {
|
|
|
- // if the fragment's nodes include a component that should be cached
|
|
|
- // return true to avoid tearing down the fragment's scope
|
|
|
- if (processFragment(frag)) {
|
|
|
- keptAliveScopes.set(oldKey, scope)
|
|
|
- return true
|
|
|
+ // inject hooks to DynamicFragment to cache components during updates
|
|
|
+ const injectKeepAliveHooks = (frag: DynamicFragment) => {
|
|
|
+ ;(frag.onBeforeTeardown || (frag.onBeforeTeardown = [])).push(
|
|
|
+ (oldKey, nodes, scope) => {
|
|
|
+ // if the fragment's nodes include a component that should be cached
|
|
|
+ // return true to avoid tearing down the fragment's scope
|
|
|
+ if (processFragment(frag)) {
|
|
|
+ keptAliveScopes.set(oldKey, scope)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ },
|
|
|
+ )
|
|
|
+ ;(frag.onBeforeMount || (frag.onBeforeMount = [])).push(() =>
|
|
|
+ cacheFragment(frag),
|
|
|
+ )
|
|
|
+ frag.getScope = key => {
|
|
|
+ const scope = keptAliveScopes.get(key)
|
|
|
+ if (scope) {
|
|
|
+ keptAliveScopes.delete(key)
|
|
|
+ return scope
|
|
|
}
|
|
|
- return false
|
|
|
- },
|
|
|
- )
|
|
|
- ;(frag.onBeforeMount || (frag.onBeforeMount = [])).push(() =>
|
|
|
- cacheFragment(frag),
|
|
|
- )
|
|
|
- frag.getScope = key => {
|
|
|
- const scope = keptAliveScopes.get(key)
|
|
|
- if (scope) {
|
|
|
- keptAliveScopes.delete(key)
|
|
|
- return scope
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // process shapeFlag
|
|
|
- if (isVaporComponent(children)) {
|
|
|
- children.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
- if (isAsyncWrapper(children)) {
|
|
|
- injectKeepAliveHooks(children.block as DynamicFragment)
|
|
|
- }
|
|
|
- } else if (isInteropFragment(children)) {
|
|
|
- children.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
- } else if (isDynamicFragment(children)) {
|
|
|
- processFragment(children)
|
|
|
- injectKeepAliveHooks(children)
|
|
|
- if (isVaporComponent(children.nodes) && isAsyncWrapper(children.nodes)) {
|
|
|
- injectKeepAliveHooks(children.nodes.block as DynamicFragment)
|
|
|
+ // process shapeFlag
|
|
|
+ if (isVaporComponent(children)) {
|
|
|
+ children.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ if (isAsyncWrapper(children)) {
|
|
|
+ injectKeepAliveHooks(children.block as DynamicFragment)
|
|
|
+ }
|
|
|
+ } else if (isInteropFragment(children)) {
|
|
|
+ children.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
|
|
+ } else if (isDynamicFragment(children)) {
|
|
|
+ processFragment(children)
|
|
|
+ injectKeepAliveHooks(children)
|
|
|
+ if (
|
|
|
+ isVaporComponent(children.nodes) &&
|
|
|
+ isAsyncWrapper(children.nodes)
|
|
|
+ ) {
|
|
|
+ injectKeepAliveHooks(children.nodes.block as DynamicFragment)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- return children
|
|
|
- },
|
|
|
-})
|
|
|
+ return children
|
|
|
+ },
|
|
|
+ })
|
|
|
|
|
|
const shouldCache = (
|
|
|
block: GenericComponentInstance | VaporFragment,
|