]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: use proxy for static props too
authorEvan You <evan@vuejs.org>
Wed, 4 Dec 2024 15:02:15 +0000 (23:02 +0800)
committerEvan You <evan@vuejs.org>
Wed, 4 Dec 2024 15:29:01 +0000 (23:29 +0800)
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/componentProps.ts

index d78ce73d09426276b36ca6e1fea8d1f3451f5ded..d05f8ffbcf24f6867359fe55521d458fe90785f3 100644 (file)
@@ -16,11 +16,11 @@ import {
 } from '@vue/runtime-dom'
 import { type Block, isBlock } from './block'
 import { pauseTracking, resetTracking } from '@vue/reactivity'
-import { EMPTY_OBJ, isFunction } from '@vue/shared'
+import { EMPTY_OBJ, hasOwn, isFunction } from '@vue/shared'
 import {
   type RawProps,
-  getDynamicPropsHandlers,
-  initStaticProps,
+  getPropsProxyHandlers,
+  normalizePropsOptions,
 } from './componentProps'
 import { setDynamicProp } from './dom/prop'
 import { renderEffect } from './renderEffect'
@@ -200,22 +200,30 @@ export class VaporComponentInstance implements GenericComponentInstance {
 
     this.rawProps = rawProps
     this.provides = this.refs = EMPTY_OBJ
-    this.emitted = this.ec = this.exposed = null
+    this.emitted = this.ec = this.exposed = this.propsDefaults = null
     this.isMounted = this.isUnmounted = this.isDeactivated = false
 
     // init props
-    this.propsDefaults = null
+    const target = rawProps || EMPTY_OBJ
+    const handlers = getPropsProxyHandlers(comp, this)
+    this.props = comp.props ? new Proxy(target, handlers[0]!) : {}
+    this.attrs = new Proxy(target, handlers[1])
+
+    // determine fallthrough
     this.hasFallthrough = false
-    if (rawProps && rawProps.$) {
-      // has dynamic props, use proxy
-      const handlers = getDynamicPropsHandlers(comp, this)
-      this.props = comp.props ? new Proxy(rawProps, handlers[0]!) : EMPTY_OBJ
-      this.attrs = new Proxy(rawProps, handlers[1])
-      this.hasFallthrough = true
-    } else {
-      this.props = {}
-      this.attrs = {}
-      this.hasFallthrough = initStaticProps(comp, rawProps, this)
+    if (rawProps) {
+      if (rawProps.$) {
+        this.hasFallthrough = true
+      } else {
+        // check if rawProps contains any keys not declared
+        const propsOptions = normalizePropsOptions(comp)[0]!
+        for (const key in rawProps) {
+          if (!hasOwn(propsOptions, key)) {
+            this.hasFallthrough = true
+            break
+          }
+        }
+      }
     }
 
     // TODO validate props
index 3d410853252ccff3403fda8e58cfdda0dc414338..3ac42dbb30aa086255a4294b72a738acffaa5cb1 100644 (file)
@@ -1,4 +1,4 @@
-import { EMPTY_ARR, NO, camelize, hasOwn, isFunction } from '@vue/shared'
+import { EMPTY_ARR, NO, hasOwn, isFunction } from '@vue/shared'
 import type { VaporComponent, VaporComponentInstance } from './component'
 import {
   type NormalizedPropsOptions,
@@ -16,70 +16,6 @@ type DynamicPropsSource =
   | (() => Record<string, unknown>)
   | Record<string, () => unknown>
 
-export function initStaticProps(
-  comp: VaporComponent,
-  rawProps: RawProps | undefined,
-  instance: VaporComponentInstance,
-): boolean {
-  let hasAttrs = false
-  const { props, attrs } = instance
-  const [propsOptions, needCastKeys] = normalizePropsOptions(comp)
-  const emitsOptions = normalizeEmitsOptions(comp)
-
-  // for dev emit check
-  if (__DEV__) {
-    instance.propsOptions = normalizePropsOptions(comp)
-    instance.emitsOptions = emitsOptions
-  }
-
-  for (const key in rawProps) {
-    const normalizedKey = camelize(key)
-    const needCast = needCastKeys && needCastKeys.includes(normalizedKey)
-    const source = rawProps[key]
-    if (propsOptions && normalizedKey in propsOptions) {
-      Object.defineProperty(props, normalizedKey, {
-        enumerable: true,
-        get: needCast
-          ? () =>
-              resolvePropValue(
-                propsOptions,
-                normalizedKey,
-                source(),
-                instance,
-                resolveDefault,
-              )
-          : source,
-      })
-    } else if (!isEmitListener(emitsOptions, key)) {
-      Object.defineProperty(attrs, key, {
-        enumerable: true,
-        get: source,
-      })
-      hasAttrs = true
-    }
-  }
-  for (const key in propsOptions) {
-    if (!(key in props)) {
-      props[key] = resolvePropValue(
-        propsOptions,
-        key,
-        undefined,
-        instance,
-        resolveDefault,
-        true,
-      )
-    }
-  }
-  return hasAttrs
-}
-
-function resolveDefault(
-  factory: (props: Record<string, any>) => unknown,
-  instance: VaporComponentInstance,
-) {
-  return factory.call(null, instance.props)
-}
-
 // TODO optimization: maybe convert functions into computeds
 export function resolveSource(
   source: Record<string, any> | (() => Record<string, any>),
@@ -89,41 +25,37 @@ export function resolveSource(
 
 const passThrough = (val: any) => val
 
-export function getDynamicPropsHandlers(
+export function getPropsProxyHandlers(
   comp: VaporComponent,
   instance: VaporComponentInstance,
 ): [ProxyHandler<RawProps> | null, ProxyHandler<RawProps>] {
   if (comp.__propsHandlers) {
     return comp.__propsHandlers
   }
-  let normalizedKeys: string[] | undefined
   const propsOptions = normalizePropsOptions(comp)[0]
   const emitsOptions = normalizeEmitsOptions(comp)
   const isProp = propsOptions ? (key: string) => hasOwn(propsOptions, key) : NO
+  const castProp = propsOptions
+    ? (key: string, value: any, isAbsent = false) =>
+        resolvePropValue(
+          propsOptions,
+          key as string,
+          value,
+          instance,
+          resolveDefault,
+          isAbsent,
+        )
+    : passThrough
 
   const getProp = (target: RawProps, key: string, asProp: boolean) => {
-    if (key === '$') return
     if (asProp) {
-      if (!isProp(key)) return
+      if (!isProp(key) || key === '$') return
     } else if (isProp(key) || isEmitListener(emitsOptions, key)) {
       return
     }
-    const castProp = propsOptions
-      ? (value: any, isAbsent = false) =>
-          asProp
-            ? resolvePropValue(
-                propsOptions,
-                key as string,
-                value,
-                instance,
-                resolveDefault,
-                isAbsent,
-              )
-            : value
-      : passThrough
 
     if (key in target) {
-      return castProp(target[key as string]())
+      return castProp(key, target[key as string]())
     }
     const dynamicSources = target.$
     if (dynamicSources) {
@@ -134,11 +66,11 @@ export function getDynamicPropsHandlers(
         isDynamic = isFunction(source)
         source = isDynamic ? (source as Function)() : source
         if (hasOwn(source, key)) {
-          return castProp(isDynamic ? source[key] : source[key]())
+          return castProp(key, isDynamic ? source[key] : source[key]())
         }
       }
     }
-    return castProp(undefined, true)
+    return castProp(key, undefined, true)
   }
 
   const propsHandlers = propsOptions
@@ -154,8 +86,7 @@ export function getDynamicPropsHandlers(
             }
           }
         },
-        ownKeys: () =>
-          normalizedKeys || (normalizedKeys = Object.keys(propsOptions)),
+        ownKeys: () => Object.keys(propsOptions),
         set: NO,
         deleteProperty: NO,
       } satisfies ProxyHandler<RawProps>)
@@ -207,7 +138,9 @@ export function getDynamicPropsHandlers(
   return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
 }
 
-function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
+export function normalizePropsOptions(
+  comp: VaporComponent,
+): NormalizedPropsOptions {
   const cached = comp.__propsOptions
   if (cached) return cached
 
@@ -220,3 +153,10 @@ function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
 
   return (comp.__propsOptions = [normalized, needCastKeys])
 }
+
+function resolveDefault(
+  factory: (props: Record<string, any>) => unknown,
+  instance: VaporComponentInstance,
+) {
+  return factory.call(null, instance.props)
+}