Skip to content

Commit fbe8932

Browse files
authored
Revert "perf(runtime-vapor): cache dynamic prop/attr and slot function source…" (#14194)
This reverts commit 5ee8913.
1 parent 79aa9db commit fbe8932

File tree

3 files changed

+26
-184
lines changed

3 files changed

+26
-184
lines changed

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

Lines changed: 0 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -591,157 +591,4 @@ describe('component: props', () => {
591591
render({ msg: () => 'test' })
592592
expect(`Invalid prop name: "$foo"`).toHaveBeenWarned()
593593
})
594-
595-
describe('dynamic props source caching', () => {
596-
test('v-bind object should be cached when child accesses multiple props', () => {
597-
let sourceCallCount = 0
598-
const obj = ref({ foo: 1, bar: 2, baz: 3 })
599-
600-
const t0 = template('<div></div>')
601-
const Child = defineVaporComponent({
602-
props: ['foo', 'bar', 'baz'],
603-
setup(props: any) {
604-
const n0 = t0()
605-
// Child component accesses multiple props
606-
renderEffect(() => {
607-
setElementText(n0, `${props.foo}-${props.bar}-${props.baz}`)
608-
})
609-
return n0
610-
},
611-
})
612-
613-
const { host } = define({
614-
setup() {
615-
return createComponent(Child, {
616-
$: [
617-
() => {
618-
sourceCallCount++
619-
return obj.value
620-
},
621-
],
622-
})
623-
},
624-
}).render()
625-
626-
expect(host.innerHTML).toBe('<div>1-2-3</div>')
627-
// Source should only be called once even though 3 props are accessed
628-
expect(sourceCallCount).toBe(1)
629-
})
630-
631-
test('v-bind object should update when source changes', async () => {
632-
let sourceCallCount = 0
633-
const obj = ref({ foo: 1, bar: 2 })
634-
635-
const t0 = template('<div></div>')
636-
const Child = defineVaporComponent({
637-
props: ['foo', 'bar'],
638-
setup(props: any) {
639-
const n0 = t0()
640-
renderEffect(() => {
641-
setElementText(n0, `${props.foo}-${props.bar}`)
642-
})
643-
return n0
644-
},
645-
})
646-
647-
const { host } = define({
648-
setup() {
649-
return createComponent(Child, {
650-
$: [
651-
() => {
652-
sourceCallCount++
653-
return obj.value
654-
},
655-
],
656-
})
657-
},
658-
}).render()
659-
660-
expect(host.innerHTML).toBe('<div>1-2</div>')
661-
expect(sourceCallCount).toBe(1)
662-
663-
// Update source
664-
obj.value = { foo: 10, bar: 20 }
665-
await nextTick()
666-
667-
expect(host.innerHTML).toBe('<div>10-20</div>')
668-
// Should be called again after source changes
669-
expect(sourceCallCount).toBe(2)
670-
})
671-
672-
test('v-bind object should be cached when child accesses multiple attrs', () => {
673-
let sourceCallCount = 0
674-
const obj = ref({ foo: 1, bar: 2, baz: 3 })
675-
676-
const t0 = template('<div></div>')
677-
const Child = defineVaporComponent({
678-
// No props declaration - all become attrs
679-
setup(_: any, { attrs }: any) {
680-
const n0 = t0()
681-
renderEffect(() => {
682-
setElementText(n0, `${attrs.foo}-${attrs.bar}-${attrs.baz}`)
683-
})
684-
return n0
685-
},
686-
})
687-
688-
const { host } = define({
689-
setup() {
690-
return createComponent(Child, {
691-
$: [
692-
() => {
693-
sourceCallCount++
694-
return obj.value
695-
},
696-
],
697-
})
698-
},
699-
}).render()
700-
701-
expect(host.innerHTML).toBe('<div foo="1" bar="2" baz="3">1-2-3</div>')
702-
// Source should only be called once
703-
expect(sourceCallCount).toBe(1)
704-
})
705-
706-
test('mixed static and dynamic props', async () => {
707-
let sourceCallCount = 0
708-
const obj = ref({ foo: 1 })
709-
710-
const t0 = template('<div></div>')
711-
const Child = defineVaporComponent({
712-
props: ['id', 'foo', 'class'],
713-
setup(props: any) {
714-
const n0 = t0()
715-
renderEffect(() => {
716-
setElementText(n0, `${props.id}-${props.foo}-${props.class}`)
717-
})
718-
return n0
719-
},
720-
})
721-
722-
const { host } = define({
723-
setup() {
724-
return createComponent(Child, {
725-
id: () => 'static',
726-
$: [
727-
() => {
728-
sourceCallCount++
729-
return obj.value
730-
},
731-
{ class: () => 'bar' },
732-
],
733-
})
734-
},
735-
}).render()
736-
737-
expect(host.innerHTML).toBe('<div>static-1-bar</div>')
738-
expect(sourceCallCount).toBe(1)
739-
740-
obj.value = { foo: 2 }
741-
await nextTick()
742-
743-
expect(host.innerHTML).toBe('<div>static-2-bar</div>')
744-
expect(sourceCallCount).toBe(2)
745-
})
746-
})
747594
})

packages/runtime-vapor/src/componentProps.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
validateProps,
2121
warn,
2222
} from '@vue/runtime-dom'
23-
import { type ComputedRef, ReactiveFlags, computed } from '@vue/reactivity'
23+
import { ReactiveFlags } from '@vue/reactivity'
2424
import { normalizeEmitsOptions } from './componentEmits'
2525
import { renderEffect } from './renderEffect'
2626
import { pauseTracking, resetTracking } from '@vue/reactivity'
@@ -35,24 +35,11 @@ export type DynamicPropsSource =
3535
| (() => Record<string, unknown>)
3636
| Record<string, () => unknown>
3737

38+
// TODO optimization: maybe convert functions into computeds
3839
export function resolveSource(
3940
source: Record<string, any> | (() => Record<string, any>),
4041
): Record<string, any> {
41-
return isFunction(source)
42-
? resolveFunctionSource(source as () => Record<string, any>)
43-
: source
44-
}
45-
46-
/**
47-
* Resolve a function source with computed caching.
48-
*/
49-
export function resolveFunctionSource<T>(
50-
source: (() => T) & { _cache?: ComputedRef<T> },
51-
): T {
52-
if (!source._cache) {
53-
source._cache = computed(source)
54-
}
55-
return source._cache.value
42+
return isFunction(source) ? source() : source
5643
}
5744

5845
export function getPropsProxyHandlers(
@@ -91,11 +78,7 @@ export function getPropsProxyHandlers(
9178
while (i--) {
9279
source = dynamicSources[i]
9380
isDynamic = isFunction(source)
94-
source = isDynamic
95-
? (resolveFunctionSource(
96-
source as () => Record<string, unknown>,
97-
) as any)
98-
: source
81+
source = isDynamic ? (source as Function)() : source
9982
for (rawKey in source) {
10083
if (camelize(rawKey) === key) {
10184
return resolvePropValue(
@@ -222,11 +205,7 @@ export function getAttrFromRawProps(rawProps: RawProps, key: string): unknown {
222205
while (i--) {
223206
source = dynamicSources[i]
224207
isDynamic = isFunction(source)
225-
source = isDynamic
226-
? (resolveFunctionSource(
227-
source as () => Record<string, unknown>,
228-
) as any)
229-
: source
208+
source = isDynamic ? (source as Function)() : source
230209
if (source && hasOwn(source, key)) {
231210
const value = isDynamic ? source[key] : source[key]()
232211
if (merged) {
@@ -358,7 +337,7 @@ export function resolveDynamicProps(props: RawProps): Record<string, unknown> {
358337
if (props.$) {
359338
for (const source of props.$) {
360339
const isDynamic = isFunction(source)
361-
const resolved = isDynamic ? resolveFunctionSource(source) : source
340+
const resolved = isDynamic ? source() : source
362341
for (const key in resolved) {
363342
const value = isDynamic ? resolved[key] : (resolved[key] as Function)()
364343
if (key === 'class' || key === 'style') {

packages/runtime-vapor/src/componentSlots.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
2+
import { type ComputedRef, computed } from '@vue/reactivity'
23
import { type Block, type BlockFn, insert, setScopeId } from './block'
3-
import { rawPropsProxyHandlers, resolveFunctionSource } from './componentProps'
4+
import { rawPropsProxyHandlers } from './componentProps'
45
import {
56
type GenericComponentInstance,
67
currentInstance,
@@ -51,9 +52,24 @@ export type StaticSlots = Record<string, VaporSlot>
5152

5253
export type VaporSlot = BlockFn
5354
export type DynamicSlot = { name: string; fn: VaporSlot }
54-
export type DynamicSlotFn = () => DynamicSlot | DynamicSlot[]
55+
export type DynamicSlotFn = (() => DynamicSlot | DynamicSlot[]) & {
56+
_cache?: ComputedRef<DynamicSlot | DynamicSlot[]>
57+
}
5558
export type DynamicSlotSource = StaticSlots | DynamicSlotFn
5659

60+
/**
61+
* Get cached result of a DynamicSlotFn.
62+
* Uses computed to cache the result and avoid redundant calls.
63+
*/
64+
function resolveDynamicSlot(
65+
source: DynamicSlotFn,
66+
): DynamicSlot | DynamicSlot[] {
67+
if (!source._cache) {
68+
source._cache = computed(source)
69+
}
70+
return source._cache.value
71+
}
72+
5773
export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
5874
get: getSlot,
5975
has: (target, key: string) => !!getSlot(target, key),
@@ -74,7 +90,7 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
7490
keys = keys.filter(k => k !== '$')
7591
for (const source of dynamicSources) {
7692
if (isFunction(source)) {
77-
const slot = resolveFunctionSource(source)
93+
const slot = resolveDynamicSlot(source)
7894
if (isArray(slot)) {
7995
for (const s of slot) keys.push(String(s.name))
8096
} else {
@@ -103,7 +119,7 @@ export function getSlot(
103119
while (i--) {
104120
source = dynamicSources[i]
105121
if (isFunction(source)) {
106-
const slot = resolveFunctionSource(source)
122+
const slot = resolveDynamicSlot(source)
107123
if (slot) {
108124
if (isArray(slot)) {
109125
for (const s of slot) {

0 commit comments

Comments
 (0)