functioninitData (vm: Component) { let data = vm.$options.data // 把传入的 options 的 data 对象赋值到 vm._data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
...省略
// proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } // 判断props和data的属性命名是否冲突 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 ) } elseif (!isReserved(key)) { // vue实例代理属性 proxy(vm, `_data`, key) } } // observe data 监测数据变化 observe(data, true/* asRootData */) }
我们简单这看下, 一开始先判断传入的 options 的 data 属性是否为函数类型, 如果是则执行 getData 方法执行 data 函数获取对应的数据对象, 接着获取这个数据对象的 keys 并进行遍历, 这里遍历的目的有两个:
对 data 和 props 进行属性命名校验检测
代理 vm 实例的属性获取到 vm._data, 这也就是我们为什么可以通过 this.user.name 获取到我们定义的数据对象的属性值。
/** * Observer 用于给对象的属性添加getter和setter,用于进行依赖收集和更新下发 */ exportclassObserver{ value: any; dep: Dep; vmCount: number; // number of vms that have this object as root $data
/** * Walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } }
/** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
Observer 的够着函数通过 def 函数, 给传入的 value 这个数据对象定义 ob 属性, 而对应这个属性的值就当前这个 Observer 的实例化对象。 def 函数的功能我们可以看下:
如果不为数组, 那也就是 value 为对象类型, 调用 walk 方法来遍历 value 的属性。 这时候, 我们会发现如果我们刚才通过 value.ob = this 来定义 value 的 ob 属性, 会导致 ob 是可枚举, 这里遍历可以枚举出 ob, 但是 ob 我们没必要对其进行响应式对象初始化, 所以也就是为什么要通过 Object.defineProperty 把 ob 定义为不可枚举的属性。遍历过程其实就是把当前 value 的每个属性的值传参进行 defineReactive 调用。