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数据对象转换成响应式的。