]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf: optimize props resolution
authorEvan You <yyx990803@gmail.com>
Fri, 13 Dec 2019 03:07:48 +0000 (22:07 -0500)
committerEvan You <yyx990803@gmail.com>
Fri, 13 Dec 2019 03:07:48 +0000 (22:07 -0500)
Store the keys for props that need default or boolean casting
during normalization, so that later we only need to iterate
through this array instead of the entire props object.

packages/runtime-core/src/componentProps.ts

index 9d9f4775f2b0cd3594267fd201d3ee58357e5632..54d67ed4d35d0b2f42d8d64d8d7c327aa4c450a5 100644 (file)
@@ -80,7 +80,9 @@ type NormalizedProp =
       [BooleanFlags.shouldCastTrue]?: boolean
     })
 
-type NormalizedPropsOptions = Record<string, NormalizedProp>
+// normalized value is a tuple of the actual normalized options
+// and an array of prop keys that need value casting (booleans and defaults)
+type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]]
 
 // resolve raw VNode data.
 // - filter out reserved keys (key, ref, slots)
@@ -96,11 +98,11 @@ export function resolveProps(
   _options: ComponentPropsOptions | void
 ) {
   const hasDeclaredProps = _options != null
-  const options = normalizePropsOptions(_options)!
   if (!rawProps && !hasDeclaredProps) {
     return
   }
 
+  const { 0: options, 1: needCastKeys } = normalizePropsOptions(_options)!
   const props: Data = {}
   let attrs: Data | undefined = void 0
 
@@ -135,9 +137,10 @@ export function resolveProps(
       }
     }
   }
-  // set default values, cast booleans & run validators
   if (hasDeclaredProps) {
-    for (const key in options) {
+    // set default values & cast booleans
+    for (let i = 0; i < needCastKeys.length; i++) {
+      const key = needCastKeys[i]
       let opt = options[key]
       if (opt == null) continue
       const isAbsent = !hasOwn(props, key)
@@ -159,15 +162,19 @@ export function resolveProps(
           setProp(key, true)
         }
       }
-      // runtime validation
-      if (__DEV__ && rawProps) {
+    }
+    // validation
+    if (__DEV__ && rawProps) {
+      for (const key in options) {
+        let opt = options[key]
+        if (opt == null) continue
         let rawValue
         if (!(key in rawProps) && hyphenate(key) in rawProps) {
           rawValue = rawProps[hyphenate(key)]
         } else {
           rawValue = rawProps[key]
         }
-        validateProp(key, toRaw(rawValue), opt, isAbsent)
+        validateProp(key, toRaw(rawValue), opt, !hasOwn(props, key))
       }
     }
   } else {
@@ -197,19 +204,22 @@ export function resolveProps(
   instance.attrs = options ? attrs || EMPTY_OBJ : props
 }
 
-const normalizationMap = new WeakMap()
+const normalizationMap = new WeakMap<
+  ComponentPropsOptions,
+  NormalizedPropsOptions
+>()
 
 function normalizePropsOptions(
   raw: ComponentPropsOptions | void
-): NormalizedPropsOptions | null {
+): NormalizedPropsOptions {
   if (!raw) {
-    return null
+    return [] as any
   }
   if (normalizationMap.has(raw)) {
-    return normalizationMap.get(raw)
+    return normalizationMap.get(raw)!
   }
-  const normalized: NormalizedPropsOptions = {}
-  normalizationMap.set(raw, normalized)
+  const options: NormalizedPropsOptions[0] = {}
+  const needCastKeys: NormalizedPropsOptions[1] = []
   if (isArray(raw)) {
     for (let i = 0; i < raw.length; i++) {
       if (__DEV__ && !isString(raw[i])) {
@@ -217,7 +227,7 @@ function normalizePropsOptions(
       }
       const normalizedKey = camelize(raw[i])
       if (normalizedKey[0] !== '$') {
-        normalized[normalizedKey] = EMPTY_OBJ
+        options[normalizedKey] = EMPTY_OBJ
       } else if (__DEV__) {
         warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
       }
@@ -230,19 +240,25 @@ function normalizePropsOptions(
       const normalizedKey = camelize(key)
       if (normalizedKey[0] !== '$') {
         const opt = raw[key]
-        const prop: NormalizedProp = (normalized[normalizedKey] =
+        const prop: NormalizedProp = (options[normalizedKey] =
           isArray(opt) || isFunction(opt) ? { type: opt } : opt)
         if (prop != null) {
           const booleanIndex = getTypeIndex(Boolean, prop.type)
           const stringIndex = getTypeIndex(String, prop.type)
           prop[BooleanFlags.shouldCast] = booleanIndex > -1
           prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
+          // if the prop needs boolean casting or default value
+          if (booleanIndex > -1 || hasOwn(prop, 'default')) {
+            needCastKeys.push(normalizedKey)
+          }
         }
       } else if (__DEV__) {
         warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
       }
     }
   }
+  const normalized: NormalizedPropsOptions = [options, needCastKeys]
+  normalizationMap.set(raw, normalized)
   return normalized
 }