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,所以这里会进行深度观测。