Skip to content

Commit 466ec98

Browse files
committed
perf(runtime-vapor): tree-shake unused keep-alive paths
1 parent 9b3429e commit 466ec98

10 files changed

Lines changed: 128 additions & 66 deletions

File tree

packages/runtime-vapor/src/apiCreateDynamicComponent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
import { DynamicFragment, type VaporFragment } from './fragment'
3333
import type { KeepAliveInstance } from './components/KeepAlive'
3434
import { isInteropEnabled } from './vdomInteropState'
35+
import { enableKeepAlive } from './keepAlive'
3536

3637
export function createDynamicComponent(
3738
getter: () => any,
@@ -62,6 +63,7 @@ export function createDynamicComponent(
6263
// Handles VNodes passed from VDOM components (e.g., `h(VaporComp)` from slots)
6364
if (isInteropEnabled && appContext.vapor && isVNode(value)) {
6465
if (isKeepAlive(currentInstance)) {
66+
enableKeepAlive()
6567
const frag = (
6668
currentInstance as KeepAliveInstance
6769
).ctx.getCachedComponent(value.type, value.key) as VaporFragment

packages/runtime-vapor/src/apiDefineAsyncComponent.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from './dom/hydration'
2828
import type { TransitionOptions } from './block'
2929
import { _next } from './dom/node'
30+
import { isKeepAliveEnabled } from './keepAlive'
3031

3132
/*@ __NO_SIDE_EFFECTS__ */
3233
export function defineVaporAsyncComponent<T extends VaporComponent>(
@@ -185,7 +186,9 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
185186

186187
frag.update(render)
187188
// Manually trigger cacheBlock for KeepAlive
188-
if (frag.keepAliveCtx) frag.keepAliveCtx.cacheBlock()
189+
if (isKeepAliveEnabled && frag.keepAliveCtx) {
190+
frag.keepAliveCtx.cacheBlock()
191+
}
189192
})
190193

191194
return frag

packages/runtime-vapor/src/component.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ import {
108108
import type { KeepAliveInstance } from './components/KeepAlive'
109109
import {
110110
currentKeepAliveCtx,
111+
isKeepAliveEnabled,
111112
setCurrentKeepAliveCtx,
112-
} from './components/KeepAlive'
113+
} from './keepAlive'
113114
import {
114115
insertionAnchor,
115116
insertionParent,
@@ -313,6 +314,7 @@ export function createComponent(
313314

314315
// keep-alive
315316
if (
317+
isKeepAliveEnabled &&
316318
currentInstance &&
317319
currentInstance.vapor &&
318320
isKeepAlive(currentInstance)
@@ -374,7 +376,11 @@ export function createComponent(
374376
// handle currentKeepAliveCtx for component boundary isolation
375377
// AsyncWrapper should NOT clear currentKeepAliveCtx so its internal
376378
// DynamicFragment can capture it
377-
if (currentKeepAliveCtx && !isAsyncWrapper(instance)) {
379+
if (
380+
isKeepAliveEnabled &&
381+
currentKeepAliveCtx &&
382+
!isAsyncWrapper(instance)
383+
) {
378384
currentKeepAliveCtx.processShapeFlag(instance)
379385
// clear currentKeepAliveCtx so child components don't associate
380386
// with parent's KeepAlive
@@ -1006,7 +1012,10 @@ export function mountComponent(
10061012
return
10071013
}
10081014

1009-
if (instance.shapeFlag! & ShapeFlags.COMPONENT_KEPT_ALIVE) {
1015+
if (
1016+
isKeepAliveEnabled &&
1017+
instance.shapeFlag! & ShapeFlags.COMPONENT_KEPT_ALIVE
1018+
) {
10101019
;(instance.parent as KeepAliveInstance)!.ctx.activate(
10111020
instance,
10121021
parent,
@@ -1034,6 +1043,7 @@ export function mountComponent(
10341043
}
10351044
if (instance.m) queuePostFlushCb(instance.m!)
10361045
if (
1046+
isKeepAliveEnabled &&
10371047
instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE &&
10381048
instance.a
10391049
) {
@@ -1051,6 +1061,7 @@ export function unmountComponent(
10511061
): void {
10521062
// Skip unmount for kept-alive components - deactivate if called from remove()
10531063
if (
1064+
isKeepAliveEnabled &&
10541065
instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE &&
10551066
instance.parent &&
10561067
instance.parent.vapor &&

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

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,36 +36,12 @@ import { unsetRef } from '../refCleanup'
3636
import { type VaporFragment, isDynamicFragment, isFragment } from '../fragment'
3737
import type { EffectScope } from '@vue/reactivity'
3838
import { isInteropEnabled } from '../vdomInteropState'
39-
40-
export interface VaporKeepAliveContext {
41-
processShapeFlag(block: Block): CacheKey | false
42-
cacheBlock(block?: Block): void
43-
cacheScope(cacheKey: CacheKey, scopeLookupKey: any, scope: EffectScope): void
44-
getScope(key: any): EffectScope | undefined
45-
}
46-
47-
export let currentKeepAliveCtx: VaporKeepAliveContext | null = null
48-
49-
export function setCurrentKeepAliveCtx(
50-
ctx: VaporKeepAliveContext | null,
51-
): VaporKeepAliveContext | null {
52-
try {
53-
return currentKeepAliveCtx
54-
} finally {
55-
currentKeepAliveCtx = ctx
56-
}
57-
}
58-
59-
let currentCacheKey: any | undefined
60-
export function withCurrentCacheKey<T>(key: any, fn: () => T): T {
61-
const prev = currentCacheKey
62-
currentCacheKey = key
63-
try {
64-
return fn()
65-
} finally {
66-
currentCacheKey = prev
67-
}
68-
}
39+
import {
40+
type VaporKeepAliveContext,
41+
currentCacheKey,
42+
setCurrentKeepAliveCtx,
43+
withKeepAliveEnabled,
44+
} from '../keepAlive'
6945

7046
export interface KeepAliveInstance extends VaporComponentInstance {
7147
ctx: {
@@ -416,7 +392,7 @@ const VaporKeepAliveImpl = defineVaporComponent({
416392
})
417393

418394
export const VaporKeepAlive: DefineVaporComponent<{}, string, KeepAliveProps> =
419-
VaporKeepAliveImpl
395+
/*@__PURE__*/ withKeepAliveEnabled(VaporKeepAliveImpl)
420396

421397
const shouldCache = (
422398
block: GenericComponentInstance | VaporFragment,

packages/runtime-vapor/src/fragment.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ import { setBlockKey } from './helpers/setKey'
5050
import {
5151
type VaporKeepAliveContext,
5252
currentKeepAliveCtx,
53+
isKeepAliveEnabled,
5354
setCurrentKeepAliveCtx,
5455
withCurrentCacheKey,
55-
} from './components/KeepAlive'
56+
} from './keepAlive'
5657
import {
5758
applyTransitionHooks,
5859
applyTransitionLeaveHooks,
@@ -94,24 +95,32 @@ export class VaporFragment<
9495
// render context
9596
readonly renderInstance: GenericComponentInstance | null = currentInstance
9697
readonly slotOwner: VaporComponentInstance | null = currentSlotOwner
97-
readonly keepAliveCtx: VaporKeepAliveContext | null = currentKeepAliveCtx
98+
readonly keepAliveCtx?: VaporKeepAliveContext | null
9899
readonly inheritedSlotBoundary: SlotBoundaryContext | null =
99100
currentSlotBoundary
100101

101102
constructor(nodes: T) {
102103
this.nodes = nodes
104+
if (isKeepAliveEnabled) {
105+
this.keepAliveCtx = currentKeepAliveCtx
106+
}
103107
}
104108

105109
protected runWithRenderCtx<R>(fn: () => R): R {
106110
const prevInstance = setCurrentInstance(this.renderInstance)
107111
const prevSlotOwner = setCurrentSlotOwner(this.slotOwner)
108-
const prevKeepAliveCtx = setCurrentKeepAliveCtx(this.keepAliveCtx)
112+
let prevKeepAliveCtx: VaporKeepAliveContext | null = null
113+
if (isKeepAliveEnabled) {
114+
prevKeepAliveCtx = setCurrentKeepAliveCtx(this.keepAliveCtx || null)
115+
}
109116
const prevBoundary = setCurrentSlotBoundary(this.inheritedSlotBoundary)
110117
try {
111118
return fn()
112119
} finally {
113120
setCurrentSlotBoundary(prevBoundary)
114-
setCurrentKeepAliveCtx(prevKeepAliveCtx)
121+
if (isKeepAliveEnabled) {
122+
setCurrentKeepAliveCtx(prevKeepAliveCtx)
123+
}
115124
setCurrentSlotOwner(prevSlotOwner)
116125
setCurrentInstance(...prevInstance)
117126
}
@@ -213,24 +222,28 @@ export class DynamicFragment extends VaporFragment {
213222
const parent = isHydrating ? null : this.anchor.parentNode
214223
// teardown previous branch
215224
if (this.scope) {
216-
let retainScope = false
217-
const keepAliveCtx = this.keepAliveCtx
218-
219-
// if keepAliveCtx exists and processShapeFlag returns a cache key,
220-
// cache the scope and retain it.
221-
const cacheKey = keepAliveCtx
222-
? this.keyed
223-
? withCurrentCacheKey(this.current, () =>
224-
keepAliveCtx.processShapeFlag(this.nodes),
225-
)
226-
: keepAliveCtx.processShapeFlag(this.nodes)
227-
: false
228-
if (cacheKey !== false) {
229-
keepAliveCtx!.cacheScope(cacheKey, this.current, this.scope)
230-
retainScope = true
231-
}
225+
if (isKeepAliveEnabled) {
226+
let retainScope = false
227+
const keepAliveCtx = this.keepAliveCtx
228+
229+
// if keepAliveCtx exists and processShapeFlag returns a cache key,
230+
// cache the scope and retain it.
231+
if (keepAliveCtx) {
232+
const cacheKey = this.keyed
233+
? withCurrentCacheKey(this.current, () =>
234+
keepAliveCtx.processShapeFlag(this.nodes),
235+
)
236+
: keepAliveCtx.processShapeFlag(this.nodes)
237+
if (cacheKey !== false) {
238+
keepAliveCtx.cacheScope(cacheKey, this.current, this.scope)
239+
retainScope = true
240+
}
241+
}
232242

233-
if (!retainScope) {
243+
if (!retainScope) {
244+
this.scope.stop()
245+
}
246+
} else {
234247
this.scope.stop()
235248
}
236249
const mode = transition && transition.mode
@@ -309,7 +322,7 @@ export class DynamicFragment extends VaporFragment {
309322
): void {
310323
this.current = key
311324
if (render) {
312-
const keepAliveCtx = this.keepAliveCtx
325+
const keepAliveCtx = isKeepAliveEnabled ? this.keepAliveCtx : null
313326
// try to reuse the kept-alive scope
314327
const scope = keepAliveCtx && keepAliveCtx.getScope(this.current)
315328
if (scope) {

packages/runtime-vapor/src/helpers/setKey.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { isArray } from '@vue/shared'
22
import { isKeepAlive } from '@vue/runtime-dom'
33
import type { Block } from '../block'
44
import { isVaporComponent } from '../component'
5+
import { isKeepAliveEnabled } from '../keepAlive'
56

67
export function setBlockKey(
78
block: (Block & { $key?: any }) | null | undefined,
@@ -16,7 +17,9 @@ export function setBlockKey(
1617
// KeepAlive resolves cache keys from its child block. An outer wrapper key
1718
// (for example from v-if) must not override the child's own component type
1819
// or explicit key, otherwise cached branches will not be found again.
19-
if (!isKeepAlive(block) && block.block) setBlockKey(block.block, key)
20+
if ((!isKeepAliveEnabled || !isKeepAlive(block)) && block.block) {
21+
setBlockKey(block.block, key)
22+
}
2023
} else if (isArray(block)) {
2124
if (block.length === 1) {
2225
setBlockKey(block[0], key)

packages/runtime-vapor/src/hmr.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from './component'
1717
import { isArray } from '@vue/shared'
1818
import { isFragment } from './fragment'
19+
import { isKeepAliveEnabled } from './keepAlive'
1920

2021
export function hmrRerender(instance: VaporComponentInstance): void {
2122
const normalized = normalizeBlock(instance.block)
@@ -38,7 +39,7 @@ export function hmrReload(
3839
): void {
3940
// If parent is KeepAlive, rerender it so new component goes through
4041
// KeepAlive's slot rendering flow to receive activated hooks properly
41-
if (instance.parent && isKeepAlive(instance.parent)) {
42+
if (isKeepAliveEnabled && instance.parent && isKeepAlive(instance.parent)) {
4243
instance.parent.hmrRerender!()
4344
return
4445
}

packages/runtime-vapor/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,4 @@ export { VaporTransitionGroup } from './components/TransitionGroup'
8484
export type { VaporComponent, VaporComponentOptions } from './component'
8585
export type { VaporSlot } from './componentSlots'
8686
export type { VaporTransitionHooks } from './block'
87-
export type { VaporKeepAliveContext } from './components/KeepAlive'
87+
export type { VaporKeepAliveContext } from './keepAlive'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { EffectScope } from '@vue/reactivity'
2+
import type { Block } from './block'
3+
4+
export interface VaporKeepAliveContext {
5+
processShapeFlag(block: Block): any | false
6+
cacheBlock(block?: Block): void
7+
cacheScope(cacheKey: any, scopeLookupKey: any, scope: EffectScope): void
8+
getScope(key: any): EffectScope | undefined
9+
}
10+
11+
export let isKeepAliveEnabled = false
12+
export let currentKeepAliveCtx: VaporKeepAliveContext | null = null
13+
export let currentCacheKey: any | undefined
14+
15+
export function enableKeepAlive(): void {
16+
isKeepAliveEnabled = true
17+
}
18+
19+
export function withKeepAliveEnabled<T>(value: T): T {
20+
enableKeepAlive()
21+
return value
22+
}
23+
24+
export function setCurrentKeepAliveCtx(
25+
ctx: VaporKeepAliveContext | null,
26+
): VaporKeepAliveContext | null {
27+
try {
28+
return currentKeepAliveCtx
29+
} finally {
30+
currentKeepAliveCtx = ctx
31+
}
32+
}
33+
34+
export function withCurrentCacheKey<T>(key: any, fn: () => T): T {
35+
const prev = currentCacheKey
36+
currentCacheKey = key
37+
try {
38+
return fn()
39+
} finally {
40+
currentCacheKey = prev
41+
}
42+
}

packages/runtime-vapor/src/vdomInterop.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,14 @@ import { setInteropEnabled } from './vdomInteropState'
119119
import {
120120
type KeepAliveInstance,
121121
activate,
122-
currentKeepAliveCtx,
123122
deactivate,
124-
setCurrentKeepAliveCtx,
125123
} from './components/KeepAlive'
124+
import {
125+
currentKeepAliveCtx,
126+
enableKeepAlive,
127+
isKeepAliveEnabled,
128+
setCurrentKeepAliveCtx,
129+
} from './keepAlive'
126130
import {
127131
parentSuspense as currentParentSuspense,
128132
enableSuspense,
@@ -200,8 +204,10 @@ const vaporInteropImpl: Omit<
200204
ensureVNodeHookState(instance, vnode)
201205

202206
// copy the shape flag from the vdom component if inside a keep-alive
203-
if (parentComponent && isKeepAlive(parentComponent))
207+
if (parentComponent && isKeepAlive(parentComponent)) {
208+
enableKeepAlive()
204209
instance.shapeFlag = vnode.shapeFlag
210+
}
205211

206212
if (vnode.transition) {
207213
ensureTransitionHooksRegistered()
@@ -790,7 +796,7 @@ function createVDOMComponent(
790796
frag.$key = vnode.key
791797
trackFragmentVNodeUpdates(frag, vnode)
792798

793-
if (currentKeepAliveCtx) {
799+
if (isKeepAliveEnabled && currentKeepAliveCtx) {
794800
currentKeepAliveCtx.processShapeFlag(frag)
795801
// for VDOM async components, trigger cacheBlock after resolution
796802
if ((component as any).__asyncLoader) {
@@ -1494,11 +1500,16 @@ const renderEmptyVNodes = (): VNodeArrayChildren => []
14941500
// VDOM fallback cleanup follow a different lifecycle.
14951501
function runWithFragmentRenderCtx<R>(fragment: VaporFragment, fn: () => R): R {
14961502
const prevSlotOwner = setCurrentSlotOwner(fragment.slotOwner)
1497-
const prevKeepAliveCtx = setCurrentKeepAliveCtx(fragment.keepAliveCtx)
1503+
let prevKeepAliveCtx = null
1504+
if (isKeepAliveEnabled) {
1505+
prevKeepAliveCtx = setCurrentKeepAliveCtx(fragment.keepAliveCtx || null)
1506+
}
14981507
try {
14991508
return withOwnedSlotBoundary(fragment.inheritedSlotBoundary, fn)
15001509
} finally {
1501-
setCurrentKeepAliveCtx(prevKeepAliveCtx)
1510+
if (isKeepAliveEnabled) {
1511+
setCurrentKeepAliveCtx(prevKeepAliveCtx)
1512+
}
15021513
setCurrentSlotOwner(prevSlotOwner)
15031514
}
15041515
}

0 commit comments

Comments
 (0)