Skip to content

Commit 51688e5

Browse files
edison1105Copilot
andcommitted
perf(runtime-vapor): avoid unnecessary dynamic fragment key propagation
Co-authored-by: Copilot <copilot@github.com>
1 parent a9f27c1 commit 51688e5

5 files changed

Lines changed: 123 additions & 8 deletions

File tree

packages/runtime-vapor/__tests__/if.spec.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
2+
VaporTransition,
23
child,
4+
createComponent,
35
createIf,
46
insert,
57
renderEffect,
@@ -185,6 +187,101 @@ describe('createIf', () => {
185187
expect(onUpdated).toHaveBeenCalledTimes(2)
186188
})
187189

190+
test('should not set branch block key without Transition or KeepAlive', async () => {
191+
const show = ref(true)
192+
const t0 = template('<div>foo</div>')
193+
const t1 = template('<div>bar</div>')
194+
let branch!: any
195+
196+
const { host } = define(() =>
197+
createIf(
198+
() => show.value,
199+
() => (branch = t0()),
200+
() => (branch = t1()),
201+
undefined,
202+
undefined,
203+
0,
204+
),
205+
).render()
206+
207+
expect(host.innerHTML).toBe('<div>foo</div><!--if-->')
208+
expect(branch.$key).toBeUndefined()
209+
210+
show.value = false
211+
await nextTick()
212+
213+
expect(host.innerHTML).toBe('<div>bar</div><!--if-->')
214+
expect(branch.$key).toBeUndefined()
215+
})
216+
217+
test('should not set branch block key outside Transition after Transition is used', async () => {
218+
const show = ref(true)
219+
const transitionChild = template('<span>transition</span>')
220+
const t0 = template('<div>foo</div>')
221+
const t1 = template('<div>bar</div>')
222+
let branch!: any
223+
224+
const { host } = define(() => [
225+
createComponent(
226+
VaporTransition,
227+
null,
228+
{
229+
default: () => transitionChild(),
230+
},
231+
true,
232+
),
233+
createIf(
234+
() => show.value,
235+
() => (branch = t0()),
236+
() => (branch = t1()),
237+
undefined,
238+
undefined,
239+
0,
240+
),
241+
]).render()
242+
243+
expect(host.innerHTML).toBe(
244+
'<span>transition</span><div>foo</div><!--if-->',
245+
)
246+
expect(branch.$key).toBeUndefined()
247+
248+
show.value = false
249+
await nextTick()
250+
251+
expect(host.innerHTML).toBe(
252+
'<span>transition</span><div>bar</div><!--if-->',
253+
)
254+
expect(branch.$key).toBeUndefined()
255+
})
256+
257+
test('should set branch block key inside Transition', () => {
258+
const show = ref(true)
259+
const t0 = template('<div>foo</div>')
260+
const t1 = template('<div>bar</div>')
261+
let branch!: any
262+
263+
define(() =>
264+
createComponent(
265+
VaporTransition,
266+
null,
267+
{
268+
default: () =>
269+
createIf(
270+
() => show.value,
271+
() => (branch = t0()),
272+
() => (branch = t1()),
273+
undefined,
274+
undefined,
275+
0,
276+
),
277+
},
278+
true,
279+
),
280+
).render()
281+
282+
expect(branch.$key).toBe('00')
283+
})
284+
188285
// vapor custom directives have no lifecycle hooks.
189286
test.todo('should work with directive hooks', async () => {
190287
const calls: string[] = []

packages/runtime-vapor/src/component.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ import {
130130
} from './suspense'
131131
import { isInteropEnabled } from './vdomInteropState'
132132
import { setComponentScopeId, setScopeId } from './scopeId'
133-
import { isTransitionEnabled } from './transition'
133+
import { isTransitionEnabled, isVaporTransition } from './transition'
134134

135135
export { currentInstance } from '@vue/runtime-dom'
136136

@@ -1155,10 +1155,6 @@ export function getRootElement(
11551155
}
11561156
}
11571157

1158-
function isVaporTransition(component: VaporComponent): boolean {
1159-
return getComponentName(component) === 'VaporTransition'
1160-
}
1161-
11621158
function handleSetupResult(
11631159
setupResult: any,
11641160
component: VaporComponent,

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type {
2929
TransitionOptions,
3030
VaporTransitionHooks,
3131
} from '../block'
32-
import { registerTransitionHooks } from '../transition'
32+
import { displayName, registerTransitionHooks } from '../transition'
3333
import {
3434
type FunctionalVaporComponent,
3535
type VaporComponentInstance,
@@ -51,7 +51,6 @@ import {
5151
import { type PendingVShow, setCurrentPendingVShows } from '../directives/vShow'
5252
import { isInteropEnabled } from '../vdomInteropState'
5353

54-
const displayName = 'VaporTransition'
5554
export type ResolvedTransitionBlock = (
5655
| Element
5756
| VaporFragment

packages/runtime-vapor/src/fragment.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
applyTransitionHooks,
5959
applyTransitionLeaveHooks,
6060
isTransitionEnabled,
61+
isVaporTransition,
6162
} from './transition'
6263

6364
export class VaporFragment<
@@ -170,6 +171,7 @@ export class DynamicFragment extends VaporFragment {
170171
pending?: { render?: BlockFn; key: any }
171172
anchorLabel?: string
172173
keyed?: boolean
174+
inTransition?: boolean
173175

174176
// fallthrough attrs
175177
attrs?: Record<string, any>
@@ -180,6 +182,13 @@ export class DynamicFragment extends VaporFragment {
180182
) {
181183
super([])
182184
this.keyed = keyed
185+
if (
186+
isTransitionEnabled &&
187+
currentInstance &&
188+
isVaporTransition(currentInstance.type)
189+
) {
190+
this.inTransition = true
191+
}
183192
if (isHydrating) {
184193
this.anchorLabel = anchorLabel
185194
if (locate) locateHydrationNode()
@@ -339,7 +348,13 @@ export class DynamicFragment extends VaporFragment {
339348
} finally {
340349
// propagate the fragment key onto freshly rendered nodes.
341350
const key = this.keyed ? this.current : this.$key
342-
if (key !== undefined) setBlockKey(this.nodes, key)
351+
// Only propagate branch keys when Transition or KeepAlive consumes them.
352+
if (
353+
key !== undefined &&
354+
(transition || this.inTransition || keepAliveCtx)
355+
) {
356+
setBlockKey(this.nodes, key)
357+
}
343358

344359
if (isTransitionEnabled && transition) {
345360
this.$transition = applyTransitionHooks(this.nodes, transition)

packages/runtime-vapor/src/transition.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { getComponentName } from '@vue/runtime-dom'
12
import type { Block } from './block'
23
import type { VaporTransitionHooks } from './block'
4+
import type { VaporComponent } from './component'
35

46
// Transition hooks registry for tree-shaking
57
// These are registered by Transition component when it's used
@@ -26,3 +28,9 @@ export function registerTransitionHooks(
2628
applyTransitionHooks = applyHooks
2729
applyTransitionLeaveHooks = applyLeaveHooks
2830
}
31+
32+
export const displayName = 'VaporTransition'
33+
34+
export function isVaporTransition(component: VaporComponent): boolean {
35+
return getComponentName(component) === displayName
36+
}

0 commit comments

Comments
 (0)