Vue源码解析之数据响应系统的使用(5)
isPlainObject是判断是否是一个纯对象的,如果data不是一个对象,在非生产环境下给出警告信息。
继续往下看:
// proxy data on instance
// 获取data对象的键
const keys = Object.keys(data)
// 获取props,是个对象
const props = vm.$options.props
// 获取methods,是个对象
const methods = vm.$options.methods
let i = keys.length
// 循环遍历data的键
while (i--) {
const key = keys[i]
// 如果methods存在,并且methods中存在与data对象相同的键,发出警告。data优先
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
// 如果props存在,并且props中存在与data对象相同的键,发出警告。 props优先
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) { // isReserved 函数用来检测一个字符串是否以 $ 或者 _ 开头,主要用来判断一个字段的键名是否是保留的
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
while中的两个if条件判断了props和methods中是否有和data对象相同的键,因为这三者中的属性都可以通过实例对象代理访问,如果相同就会出现冲突了。
const vm = new Vue({
props: { a: { default: 2 } }
data: { a: 1 },
methods: {
a () {
console.log(3)
}
}
})
当调用vm.a的时候,就会产生覆盖现象。为了防止这种情况出现,就在这里做了判断。
再看else if中的内容,当!isReserved(key)成立时,执行proxy(vm,_data, key)。 isReserved函数的作用是判断一个字符串是否以 $ 或者 _ 开头, 因为Vue内部的变量是以$或_开头,防止冲突。如果 key 不是以 $或 _ 开头,那么将执行 proxy 函数
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
proxy函数通过Object.defineProperty在实例对象vm上定义了与data数据字段相同的访问器属性,代理的值是vm._data上对应的属性值。当访问this.a时,实际访问的是this._data.a的值。
最后一句代码是
// observe data observe(data, true /* asRootData */)
调用observe将data数据对象转换成响应式的。

