Skip to content

Commit 88ae742

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

File tree

3 files changed

+50
-33
lines changed

3 files changed

+50
-33
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)
266+
const scope = children.scope
267+
if (scope) {
268+
keptAliveScopes.set(oldKey, scope)
269+
}
270+
},
271+
afterUpdate(newKey, nodes, scope) {
272+
cacheFragment(children)
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: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,12 @@ import {
1111
remove,
1212
} from './block'
1313
import {
14-
type GenericComponentInstance,
1514
type TransitionHooks,
1615
type VNode,
17-
currentInstance,
18-
isKeepAlive,
1916
queuePostFlushCb,
2017
} from '@vue/runtime-dom'
2118
import type { VaporComponentInstance } from './component'
2219
import type { NodeRef } from './apiTemplateRef'
23-
import type { KeepAliveInstance } from './components/KeepAlive'
2420
import {
2521
applyTransitionHooks,
2622
applyTransitionLeaveHooks,
@@ -33,6 +29,12 @@ import {
3329
locateHydrationNode,
3430
} from './dom/hydration'
3531

32+
export interface FragmentHooks {
33+
getScope(key: any): EffectScope | undefined
34+
beforeUpdate(oldKey: any, newKey: any): void
35+
afterUpdate(newKey: any, nodes: Block, scope: EffectScope): void
36+
}
37+
3638
export class VaporFragment<T extends Block = Block>
3739
implements TransitionOptions
3840
{
@@ -73,8 +75,7 @@ export class DynamicFragment extends VaporFragment {
7375
current?: BlockFn
7476
fallback?: BlockFn
7577
anchorLabel?: string
76-
inKeepAlive?: boolean
77-
keptAliveScopes?: Map<any, EffectScope>
78+
hooks?: FragmentHooks
7879

7980
constructor(anchorLabel?: string) {
8081
super([])
@@ -97,21 +98,17 @@ export class DynamicFragment extends VaporFragment {
9798
const prevSub = setActiveSub()
9899
const parent = isHydrating ? null : this.anchor.parentNode
99100
const transition = this.$transition
100-
const instance = currentInstance!
101-
this.inKeepAlive = isKeepAlive(instance)
102101
// teardown previous branch
103102
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)
103+
if (this.hooks) {
104+
this.hooks.beforeUpdate(this.current, key)
108105
} else {
109106
this.scope.stop()
110107
}
111108
const mode = transition && transition.mode
112109
if (mode) {
113110
applyTransitionLeaveHooks(this.nodes, transition, () =>
114-
this.render(render, instance, transition, parent),
111+
this.render(render, transition, parent),
115112
)
116113
parent && remove(this.nodes, parent)
117114
if (mode === 'out-in') {
@@ -123,7 +120,7 @@ export class DynamicFragment extends VaporFragment {
123120
}
124121
}
125122

126-
this.render(render, instance, transition, parent)
123+
this.render(render, transition, parent)
127124

128125
if (this.fallback) {
129126
// set fallback for nested fragments
@@ -155,27 +152,22 @@ export class DynamicFragment extends VaporFragment {
155152

156153
private render(
157154
render: BlockFn | undefined,
158-
instance: GenericComponentInstance,
159155
transition: VaporTransitionHooks | undefined,
160156
parent: ParentNode | null,
161157
) {
162158
if (render) {
163159
// 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
160+
const scope = this.hooks && this.hooks.getScope(this.current)
168161
if (scope) {
169162
this.scope = scope
170-
this.keptAliveScopes!.delete(this.current!)
171-
this.scope.resume()
172163
} else {
173164
this.scope = new EffectScope()
174165
}
175166

176167
this.nodes = this.scope.run(render) || []
177-
if (this.inKeepAlive) {
178-
;(instance as KeepAliveInstance).cacheFragment(this)
168+
169+
if (this.hooks) {
170+
this.hooks.afterUpdate(this.current, this.nodes, this.scope)
179171
}
180172
if (transition) {
181173
this.$transition = applyTransitionHooks(this.nodes, transition)
@@ -287,3 +279,9 @@ function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
287279
export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
288280
return val instanceof VaporFragment
289281
}
282+
283+
export function isDynamicFragment(
284+
val: NonNullable<unknown>,
285+
): val is DynamicFragment {
286+
return val instanceof DynamicFragment
287+
}

0 commit comments

Comments
 (0)