fix(runtime-core): respect props from mixins and extends

fix #1236, close #1250
This commit is contained in:
Evan You
2020-06-09 11:27:40 -04:00
parent dc986addd9
commit 2417a0cb30
5 changed files with 95 additions and 40 deletions

View File

@@ -17,7 +17,12 @@ import {
def
} from '@vue/shared'
import { warn } from './warning'
import { Data, ComponentInternalInstance } from './component'
import {
Data,
ComponentInternalInstance,
ComponentOptions,
Component
} from './component'
import { isEmitListener } from './componentEmits'
import { InternalObjectKey } from './vnode'
@@ -96,7 +101,7 @@ type 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[]]
export type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]]
export function initProps(
instance: ComponentInternalInstance,
@@ -108,17 +113,16 @@ export function initProps(
const attrs: Data = {}
def(attrs, InternalObjectKey, 1)
setFullProps(instance, rawProps, props, attrs)
const options = instance.type.props
// validation
if (__DEV__ && options && rawProps) {
validateProps(props, options)
if (__DEV__) {
validateProps(props, instance.type)
}
if (isStateful) {
// stateful
instance.props = isSSR ? props : shallowReactive(props)
} else {
if (!options) {
if (!instance.type.props) {
// functional w/ optional props, props === attrs
instance.props = attrs
} else {
@@ -140,9 +144,8 @@ export function updateProps(
attrs,
vnode: { patchFlag }
} = instance
const rawOptions = instance.type.props
const rawCurrentProps = toRaw(props)
const [options] = normalizePropsOptions(rawOptions)
const [options] = normalizePropsOptions(instance.type)
if ((optimized || patchFlag > 0) && !(patchFlag & PatchFlags.FULL_PROPS)) {
if (patchFlag & PatchFlags.PROPS) {
@@ -211,8 +214,8 @@ export function updateProps(
}
}
if (__DEV__ && rawOptions && rawProps) {
validateProps(props, rawOptions)
if (__DEV__ && rawProps) {
validateProps(props, instance.type)
}
}
@@ -222,9 +225,7 @@ function setFullProps(
props: Data,
attrs: Data
) {
const [options, needCastKeys] = normalizePropsOptions(
instance.type.props
)
const [options, needCastKeys] = normalizePropsOptions(instance.type)
const emits = instance.type.emits
if (rawProps) {
@@ -292,16 +293,38 @@ function resolvePropValue(
}
export function normalizePropsOptions(
raw: ComponentPropsOptions | undefined
comp: Component
): NormalizedPropsOptions | [] {
if (!raw) {
return EMPTY_ARR as any
}
if ((raw as any)._n) {
return (raw as any)._n
if (comp.__props) {
return comp.__props
}
const raw = comp.props
const normalized: NormalizedPropsOptions[0] = {}
const needCastKeys: NormalizedPropsOptions[1] = []
// apply mixin/extends props
let hasExtends = false
if (__FEATURE_OPTIONS__ && !isFunction(comp)) {
const extendProps = (raw: ComponentOptions) => {
const [props, keys] = normalizePropsOptions(raw)
Object.assign(normalized, props)
if (keys) needCastKeys.push(...keys)
}
if (comp.extends) {
hasExtends = true
extendProps(comp.extends)
}
if (comp.mixins) {
hasExtends = true
comp.mixins.forEach(extendProps)
}
}
if (!raw && !hasExtends) {
return (comp.__props = EMPTY_ARR)
}
if (isArray(raw)) {
for (let i = 0; i < raw.length; i++) {
if (__DEV__ && !isString(raw[i])) {
@@ -312,7 +335,7 @@ export function normalizePropsOptions(
normalized[normalizedKey] = EMPTY_OBJ
}
}
} else {
} else if (raw) {
if (__DEV__ && !isObject(raw)) {
warn(`invalid props options`, raw)
}
@@ -337,7 +360,7 @@ export function normalizePropsOptions(
}
}
const normalizedEntry: NormalizedPropsOptions = [normalized, needCastKeys]
def(raw, '_n', normalizedEntry)
comp.__props = normalizedEntry
return normalizedEntry
}
@@ -368,9 +391,12 @@ function getTypeIndex(
return -1
}
function validateProps(props: Data, rawOptions: ComponentPropsOptions) {
/**
* dev only
*/
function validateProps(props: Data, comp: Component) {
const rawValues = toRaw(props)
const options = normalizePropsOptions(rawOptions)[0]
const options = normalizePropsOptions(comp)[0]
for (const key in options) {
let opt = options[key]
if (opt == null) continue
@@ -378,6 +404,9 @@ function validateProps(props: Data, rawOptions: ComponentPropsOptions) {
}
}
/**
* dev only
*/
function validatePropName(key: string) {
if (key[0] !== '$') {
return true
@@ -387,6 +416,9 @@ function validatePropName(key: string) {
return false
}
/**
* dev only
*/
function validateProp(
name: string,
value: unknown,
@@ -434,6 +466,9 @@ type AssertionResult = {
expectedType: string
}
/**
* dev only
*/
function assertType(value: unknown, type: PropConstructor): AssertionResult {
let valid
const expectedType = getType(type)
@@ -457,6 +492,9 @@ function assertType(value: unknown, type: PropConstructor): AssertionResult {
}
}
/**
* dev only
*/
function getInvalidTypeMessage(
name: string,
value: unknown,
@@ -485,6 +523,9 @@ function getInvalidTypeMessage(
return message
}
/**
* dev only
*/
function styleValue(value: unknown, type: string): string {
if (type === 'String') {
return `"${value}"`
@@ -495,11 +536,17 @@ function styleValue(value: unknown, type: string): string {
}
}
/**
* dev only
*/
function isExplicable(type: string): boolean {
const explicitTypes = ['string', 'number', 'boolean']
return explicitTypes.some(elem => type.toLowerCase() === elem)
}
/**
* dev only
*/
function isBoolean(...args: string[]): boolean {
return args.some(elem => elem.toLowerCase() === 'boolean')
}