Skip to content

Commit 1c0dbab

Browse files
committed
feat: Refactor KeepAlive to manage fragment scope and caching via new FragmentHooks on DynamicFragment.
1 parent e6d8582 commit 1c0dbab

File tree

3 files changed

+48
-27
lines changed

3 files changed

+48
-27
lines changed

packages/runtime-vapor/src/block.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,6 @@ export function remove(block: Block, parent?: ParentNode): void {
160160
if (block.anchor) remove(block.anchor, parent)
161161
if ((block as DynamicFragment).scope) {
162162
;(block as DynamicFragment).scope!.stop()
163-
const scopes = (block as DynamicFragment).keptAliveScopes
164-
if (scopes) {
165-
scopes.forEach(scope => scope.stop())
166-
scopes.clear()
167-
}
168163
}
169164
}
170165
}

packages/runtime-vapor/src/components/KeepAlive.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import { createElement } from '../dom/node'
2929
import {
3030
type DynamicFragment,
3131
type VaporFragment,
32+
isDynamicFragment,
3233
isFragment,
3334
} from '../fragment'
35+
import type { EffectScope } from '@vue/reactivity'
3436

3537
export interface KeepAliveInstance extends VaporComponentInstance {
3638
activate: (
@@ -44,8 +46,6 @@ export interface KeepAliveInstance extends VaporComponentInstance {
4446
comp: VaporComponent,
4547
) => VaporComponentInstance | VaporFragment | undefined
4648
getStorageContainer: () => ParentNode
47-
processFragment: (fragment: DynamicFragment) => void
48-
cacheFragment: (fragment: DynamicFragment) => void
4949
}
5050

5151
type CacheKey = VaporComponent | VNode['type']
@@ -69,6 +69,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
6969
const cache: Cache = new Map()
7070
const keys: Keys = new Set()
7171
const storageContainer = createElement('div')
72+
const keptAliveScopes = new Map<any, EffectScope>()
7273
let current: VaporComponentInstance | VaporFragment | undefined
7374

7475
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
@@ -163,6 +164,8 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
163164

164165
remove(cached, storageContainer)
165166
})
167+
keptAliveScopes.forEach(scope => scope.stop())
168+
keptAliveScopes.clear()
166169
})
167170

168171
keepAliveInstance.getStorageContainer = () => storageContainer
@@ -177,7 +180,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
177180
innerCacheBlock(instance.type, instance)
178181
}
179182

180-
keepAliveInstance.processFragment = (frag: DynamicFragment) => {
183+
const processFragment = (frag: DynamicFragment) => {
181184
const innerBlock = getInnerBlock(frag.nodes)
182185
if (!innerBlock) return
183186

@@ -199,7 +202,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
199202
}
200203
}
201204

202-
keepAliveInstance.cacheFragment = (fragment: DynamicFragment) => {
205+
const cacheFragment = (fragment: DynamicFragment) => {
203206
const innerBlock = getInnerBlock(fragment.nodes)
204207
if (!innerBlock || !shouldCache(innerBlock)) return
205208

@@ -208,7 +211,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
208211
let key: CacheKey
209212

210213
// find vdom interop fragment
211-
const frag = findInteropFragment(fragment)
214+
const frag = findInteropFragment(fragment.nodes)
212215
if (frag) {
213216
frag.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
214217
toCache = frag
@@ -256,6 +259,27 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
256259
children.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
257260
} else if (isInteropFragment(children)) {
258261
children.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
262+
} else if (isDynamicFragment(children)) {
263+
children.hooks = {
264+
beforeUpdate(oldKey, newKey) {
265+
processFragment(children as DynamicFragment)
266+
const scope = (children as DynamicFragment).scope
267+
if (scope) {
268+
keptAliveScopes.set(oldKey, scope)
269+
}
270+
},
271+
afterUpdate(newKey, nodes, scope) {
272+
cacheFragment(children as DynamicFragment)
273+
},
274+
getScope(key) {
275+
const scope = keptAliveScopes.get(key)
276+
if (scope) {
277+
keptAliveScopes.delete(key)
278+
return scope
279+
}
280+
},
281+
}
282+
cacheFragment(children)
259283
}
260284

261285
function pruneCache(filter: (name: string) => boolean) {

packages/runtime-vapor/src/fragment.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ import {
1515
type TransitionHooks,
1616
type VNode,
1717
currentInstance,
18-
isKeepAlive,
1918
queuePostFlushCb,
2019
} from '@vue/runtime-dom'
2120
import type { VaporComponentInstance } from './component'
2221
import type { NodeRef } from './apiTemplateRef'
23-
import type { KeepAliveInstance } from './components/KeepAlive'
2422
import {
2523
applyTransitionHooks,
2624
applyTransitionLeaveHooks,
@@ -33,6 +31,12 @@ import {
3331
locateHydrationNode,
3432
} from './dom/hydration'
3533

34+
export interface FragmentHooks {
35+
getScope(key: any): EffectScope | undefined
36+
beforeUpdate(oldKey: any, newKey: any): void
37+
afterUpdate(newKey: any, nodes: Block, scope: EffectScope): void
38+
}
39+
3640
export class VaporFragment<T extends Block = Block>
3741
implements TransitionOptions
3842
{
@@ -73,8 +77,7 @@ export class DynamicFragment extends VaporFragment {
7377
current?: BlockFn
7478
fallback?: BlockFn
7579
anchorLabel?: string
76-
inKeepAlive?: boolean
77-
keptAliveScopes?: Map<any, EffectScope>
80+
hooks?: FragmentHooks
7881

7982
constructor(anchorLabel?: string) {
8083
super([])
@@ -98,13 +101,10 @@ export class DynamicFragment extends VaporFragment {
98101
const parent = isHydrating ? null : this.anchor.parentNode
99102
const transition = this.$transition
100103
const instance = currentInstance!
101-
this.inKeepAlive = isKeepAlive(instance)
102104
// teardown previous branch
103105
if (this.scope) {
104-
if (this.inKeepAlive) {
105-
;(instance as KeepAliveInstance).processFragment(this)
106-
if (!this.keptAliveScopes) this.keptAliveScopes = new Map()
107-
this.keptAliveScopes.set(this.current, this.scope)
106+
if (this.hooks) {
107+
this.hooks.beforeUpdate(this.current, key)
108108
} else {
109109
this.scope.stop()
110110
}
@@ -161,21 +161,17 @@ export class DynamicFragment extends VaporFragment {
161161
) {
162162
if (render) {
163163
// For KeepAlive, try to reuse the keepAlive scope for this key
164-
const scope =
165-
this.inKeepAlive && this.keptAliveScopes
166-
? this.keptAliveScopes.get(this.current)
167-
: undefined
164+
const scope = this.hooks && this.hooks.getScope(this.current)
168165
if (scope) {
169166
this.scope = scope
170-
this.keptAliveScopes!.delete(this.current!)
171-
this.scope.resume()
172167
} else {
173168
this.scope = new EffectScope()
174169
}
175170

176171
this.nodes = this.scope.run(render) || []
177-
if (this.inKeepAlive) {
178-
;(instance as KeepAliveInstance).cacheFragment(this)
172+
173+
if (this.hooks) {
174+
this.hooks.afterUpdate(this.current, this.nodes, this.scope)
179175
}
180176
if (transition) {
181177
this.$transition = applyTransitionHooks(this.nodes, transition)
@@ -287,3 +283,9 @@ function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
287283
export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
288284
return val instanceof VaporFragment
289285
}
286+
287+
export function isDynamicFragment(
288+
val: NonNullable<unknown>,
289+
): val is DynamicFragment {
290+
return val instanceof DynamicFragment
291+
}

0 commit comments

Comments
 (0)