Explorar o código

Revert "perf(runtime-vapor): cache dynamic prop/attr and slot function source…" (#14194)

This reverts commit 5ee89131403fe963ae36fc9e576a3f92f577efab.
edison hai 4 meses
pai
achega
fbe8932252

+ 0 - 153
packages/runtime-vapor/__tests__/componentProps.spec.ts

@@ -591,157 +591,4 @@ describe('component: props', () => {
     render({ msg: () => 'test' })
     expect(`Invalid prop name: "$foo"`).toHaveBeenWarned()
   })
-
-  describe('dynamic props source caching', () => {
-    test('v-bind object should be cached when child accesses multiple props', () => {
-      let sourceCallCount = 0
-      const obj = ref({ foo: 1, bar: 2, baz: 3 })
-
-      const t0 = template('<div></div>')
-      const Child = defineVaporComponent({
-        props: ['foo', 'bar', 'baz'],
-        setup(props: any) {
-          const n0 = t0()
-          // Child component accesses multiple props
-          renderEffect(() => {
-            setElementText(n0, `${props.foo}-${props.bar}-${props.baz}`)
-          })
-          return n0
-        },
-      })
-
-      const { host } = define({
-        setup() {
-          return createComponent(Child, {
-            $: [
-              () => {
-                sourceCallCount++
-                return obj.value
-              },
-            ],
-          })
-        },
-      }).render()
-
-      expect(host.innerHTML).toBe('<div>1-2-3</div>')
-      // Source should only be called once even though 3 props are accessed
-      expect(sourceCallCount).toBe(1)
-    })
-
-    test('v-bind object should update when source changes', async () => {
-      let sourceCallCount = 0
-      const obj = ref({ foo: 1, bar: 2 })
-
-      const t0 = template('<div></div>')
-      const Child = defineVaporComponent({
-        props: ['foo', 'bar'],
-        setup(props: any) {
-          const n0 = t0()
-          renderEffect(() => {
-            setElementText(n0, `${props.foo}-${props.bar}`)
-          })
-          return n0
-        },
-      })
-
-      const { host } = define({
-        setup() {
-          return createComponent(Child, {
-            $: [
-              () => {
-                sourceCallCount++
-                return obj.value
-              },
-            ],
-          })
-        },
-      }).render()
-
-      expect(host.innerHTML).toBe('<div>1-2</div>')
-      expect(sourceCallCount).toBe(1)
-
-      // Update source
-      obj.value = { foo: 10, bar: 20 }
-      await nextTick()
-
-      expect(host.innerHTML).toBe('<div>10-20</div>')
-      // Should be called again after source changes
-      expect(sourceCallCount).toBe(2)
-    })
-
-    test('v-bind object should be cached when child accesses multiple attrs', () => {
-      let sourceCallCount = 0
-      const obj = ref({ foo: 1, bar: 2, baz: 3 })
-
-      const t0 = template('<div></div>')
-      const Child = defineVaporComponent({
-        // No props declaration - all become attrs
-        setup(_: any, { attrs }: any) {
-          const n0 = t0()
-          renderEffect(() => {
-            setElementText(n0, `${attrs.foo}-${attrs.bar}-${attrs.baz}`)
-          })
-          return n0
-        },
-      })
-
-      const { host } = define({
-        setup() {
-          return createComponent(Child, {
-            $: [
-              () => {
-                sourceCallCount++
-                return obj.value
-              },
-            ],
-          })
-        },
-      }).render()
-
-      expect(host.innerHTML).toBe('<div foo="1" bar="2" baz="3">1-2-3</div>')
-      // Source should only be called once
-      expect(sourceCallCount).toBe(1)
-    })
-
-    test('mixed static and dynamic props', async () => {
-      let sourceCallCount = 0
-      const obj = ref({ foo: 1 })
-
-      const t0 = template('<div></div>')
-      const Child = defineVaporComponent({
-        props: ['id', 'foo', 'class'],
-        setup(props: any) {
-          const n0 = t0()
-          renderEffect(() => {
-            setElementText(n0, `${props.id}-${props.foo}-${props.class}`)
-          })
-          return n0
-        },
-      })
-
-      const { host } = define({
-        setup() {
-          return createComponent(Child, {
-            id: () => 'static',
-            $: [
-              () => {
-                sourceCallCount++
-                return obj.value
-              },
-              { class: () => 'bar' },
-            ],
-          })
-        },
-      }).render()
-
-      expect(host.innerHTML).toBe('<div>static-1-bar</div>')
-      expect(sourceCallCount).toBe(1)
-
-      obj.value = { foo: 2 }
-      await nextTick()
-
-      expect(host.innerHTML).toBe('<div>static-2-bar</div>')
-      expect(sourceCallCount).toBe(2)
-    })
-  })
 })

+ 6 - 27
packages/runtime-vapor/src/componentProps.ts

@@ -20,7 +20,7 @@ import {
   validateProps,
   warn,
 } from '@vue/runtime-dom'
-import { type ComputedRef, ReactiveFlags, computed } from '@vue/reactivity'
+import { ReactiveFlags } from '@vue/reactivity'
 import { normalizeEmitsOptions } from './componentEmits'
 import { renderEffect } from './renderEffect'
 import { pauseTracking, resetTracking } from '@vue/reactivity'
@@ -35,24 +35,11 @@ export type DynamicPropsSource =
   | (() => Record<string, unknown>)
   | Record<string, () => unknown>
 
+// TODO optimization: maybe convert functions into computeds
 export function resolveSource(
   source: Record<string, any> | (() => Record<string, any>),
 ): Record<string, any> {
-  return isFunction(source)
-    ? resolveFunctionSource(source as () => Record<string, any>)
-    : source
-}
-
-/**
- * Resolve a function source with computed caching.
- */
-export function resolveFunctionSource<T>(
-  source: (() => T) & { _cache?: ComputedRef<T> },
-): T {
-  if (!source._cache) {
-    source._cache = computed(source)
-  }
-  return source._cache.value
+  return isFunction(source) ? source() : source
 }
 
 export function getPropsProxyHandlers(
@@ -91,11 +78,7 @@ export function getPropsProxyHandlers(
       while (i--) {
         source = dynamicSources[i]
         isDynamic = isFunction(source)
-        source = isDynamic
-          ? (resolveFunctionSource(
-              source as () => Record<string, unknown>,
-            ) as any)
-          : source
+        source = isDynamic ? (source as Function)() : source
         for (rawKey in source) {
           if (camelize(rawKey) === key) {
             return resolvePropValue(
@@ -222,11 +205,7 @@ export function getAttrFromRawProps(rawProps: RawProps, key: string): unknown {
     while (i--) {
       source = dynamicSources[i]
       isDynamic = isFunction(source)
-      source = isDynamic
-        ? (resolveFunctionSource(
-            source as () => Record<string, unknown>,
-          ) as any)
-        : source
+      source = isDynamic ? (source as Function)() : source
       if (source && hasOwn(source, key)) {
         const value = isDynamic ? source[key] : source[key]()
         if (merged) {
@@ -358,7 +337,7 @@ export function resolveDynamicProps(props: RawProps): Record<string, unknown> {
   if (props.$) {
     for (const source of props.$) {
       const isDynamic = isFunction(source)
-      const resolved = isDynamic ? resolveFunctionSource(source) : source
+      const resolved = isDynamic ? source() : source
       for (const key in resolved) {
         const value = isDynamic ? resolved[key] : (resolved[key] as Function)()
         if (key === 'class' || key === 'style') {

+ 20 - 4
packages/runtime-vapor/src/componentSlots.ts

@@ -1,6 +1,7 @@
 import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
+import { type ComputedRef, computed } from '@vue/reactivity'
 import { type Block, type BlockFn, insert, setScopeId } from './block'
-import { rawPropsProxyHandlers, resolveFunctionSource } from './componentProps'
+import { rawPropsProxyHandlers } from './componentProps'
 import {
   type GenericComponentInstance,
   currentInstance,
@@ -51,9 +52,24 @@ export type StaticSlots = Record<string, VaporSlot>
 
 export type VaporSlot = BlockFn
 export type DynamicSlot = { name: string; fn: VaporSlot }
-export type DynamicSlotFn = () => DynamicSlot | DynamicSlot[]
+export type DynamicSlotFn = (() => DynamicSlot | DynamicSlot[]) & {
+  _cache?: ComputedRef<DynamicSlot | DynamicSlot[]>
+}
 export type DynamicSlotSource = StaticSlots | DynamicSlotFn
 
+/**
+ * Get cached result of a DynamicSlotFn.
+ * Uses computed to cache the result and avoid redundant calls.
+ */
+function resolveDynamicSlot(
+  source: DynamicSlotFn,
+): DynamicSlot | DynamicSlot[] {
+  if (!source._cache) {
+    source._cache = computed(source)
+  }
+  return source._cache.value
+}
+
 export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
   get: getSlot,
   has: (target, key: string) => !!getSlot(target, key),
@@ -74,7 +90,7 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
       keys = keys.filter(k => k !== '$')
       for (const source of dynamicSources) {
         if (isFunction(source)) {
-          const slot = resolveFunctionSource(source)
+          const slot = resolveDynamicSlot(source)
           if (isArray(slot)) {
             for (const s of slot) keys.push(String(s.name))
           } else {
@@ -103,7 +119,7 @@ export function getSlot(
     while (i--) {
       source = dynamicSources[i]
       if (isFunction(source)) {
-        const slot = resolveFunctionSource(source)
+        const slot = resolveDynamicSlot(source)
         if (slot) {
           if (isArray(slot)) {
             for (const s of slot) {