diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 9d9f4775..54d67ed4 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -80,7 +80,9 @@ type NormalizedProp = [BooleanFlags.shouldCastTrue]?: boolean }) -type NormalizedPropsOptions = Record +// 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[]] // 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 }