Vue源码解析之数据响应系统的使用(8)

我们先来看是对象的情况,也就是执行this.walk(value)

walk函数就定义在constructor的下面

walk (obj: Object) {
 const keys = Object.keys(obj)
 for (let i = 0; i < keys.length; i++) {
  defineReactive(obj, keys[i])
 }
}

该方法就是用for循环遍历了对象的属性,并对每个属性都调用了defineReactive方法。

defineReactive 函数

defineReactive也定义在core/observer/index.js文件中,找到它的定义:

/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
 obj: Object,
 key: string,
 val: any,
 customSetter?: ?Function,
 shallow?: boolean
) {
 const dep = new Dep()

 const property = Object.getOwnPropertyDescriptor(obj, key)
 if (property && property.configurable === false) {
 return
 }

 // cater for pre-defined getter/setters
 const getter = property && property.get
 const setter = property && property.set
 if ((!getter || setter) && arguments.length === 2) {
 val = obj[key]
 }

 let childOb = !shallow && observe(val)
 Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: function reactiveGetter () {
  ...
 },
 set: function reactiveSetter (newVal) {
  ...
 }
 })
}

因代码太长,省略了部分内容,之后我们再具体看。该函数的主要作用就是将数据对象的数据属性转换为访问器属性

函数体内首先定义了dep常量,它的值是Dep实例,用来收集对应字段的依赖。

接下来是这样一段代码:

const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
 return
}

先通过Object.getOwnPropertyDescriptor获取字段的属性描述对象,再判断该字段是否是可配置的,如果不可配置,直接返回。因为不可配置的属性是不能通过Object.defineProperty改变其属性定义的。

再往下接着看:

// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
 val = obj[key]
}

先保存属性描述对象里面的get和set方法。如果这个属性已经是访问器属性了,那它就存在get或set方法了,下面的操作会使用Object.defineProperty重写get和set方法,为了不影响原来的读写操作,就先缓存setter/getter。

接下来是一个if判断,如果满足条件的话,就读取该属性的值。

再下面是这样一句代码:

let childOb = !shallow && observe(val)

因为属性值val也可能是一个对象,所以调用observe继续观测。但前面有一个条件,只有当shallow为假时才会进行深度观测。shallow是defineReactive的第五个参数,我们在walk中调用该函数时并没有传递该参数,所以这里它的值是undefined。!shallow的是true,所以这里会进行深度观测。