function initProps (vm: Component, propsOptions: Object) {
// 根组件可能存在的 propsData 数据
const propsData = vm.$options.propsData || {}
// 普通组件使用方式时传递的 props 数据
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
// props 的 key 值集合
const keys = vm.$options._propKeys = []
// 当前实例是否是根实例
const isRoot = !vm.$parent
// root instance props should be converted
// 如果是不是根实例,则取消响应式更新
if (!isRoot) {
toggleObserving(false)
}
// 遍历定义的 props 选项
for (const key in propsOptions) {
keys.push(key)
// 用来校验名字(key)给定的 prop 数据是否符合预期的类型,并返回相应 prop 的值(或默认值)。
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
// 非生产环境时的一些校验报错信息,更有助于开发者 debug
if (process.env.NODE_ENV !== 'production') {
// 如果相关 key 值以及预设,则抛出错误
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
// 如果试图更新 props 值则抛出错误
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
// 简单地使 props 具备响应式(非深度)
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
// 代理访问那些未定义的 props
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
validateProp
export function validateProp (
key: string,
propOptions: Object,
propsData: Object,
vm?: Component
): any {
// 取出 prop 配置
const prop = propOptions[key]
// 当前 prop 是否为传递
const absent = !hasOwn(propsData, key)
// 取出对应的值
let value = propsData[key]
// 判断当前 prop 类型是否有 boolean 类型
const booleanIndex = getTypeIndex(Boolean, prop.type)
// 如果是或者有 boolean 类型
if (booleanIndex > -1) {
if (absent && !hasOwn(prop, 'default')) {
// 未传值且没有设置默认值则设置成 false
value = false
} else if (value === '' || value === hyphenate(key)) {
// 当 prop 是一个空字符串,或者是一个名字由驼峰转连字符后与值为相同字符串的 prop
// 并且 boolean 的权重高于 string,则设置成 true
const stringIndex = getTypeIndex(String, prop.type)
if (stringIndex < 0 || booleanIndex < stringIndex) {
value = true
}
}
}
// 如果值为 undefined
if (value === undefined) {
// 获取默认值
value = getPropDefaultValue(vm, prop, key)
// 将默认值变成响应式数据
const prevShouldObserve = shouldObserve
toggleObserving(true)
observe(value)
toggleObserving(prevShouldObserve)
}
// props 的验证抛错
if (
process.env.NODE_ENV !== 'production' &&
// skip validation for weex recycle-list child component props
!(__WEEX__ && isObject(value) && ('@binding' in value))
) {
assertProp(prop, key, value, vm, absent)
}
return value
}