vue做为一个MVVM框架,是若是对数据属性实现响应式的呢?经过深刻研究,发现它是经过Object.defineProperty(只支持纯对象)绑定get,set来实现的,下面就来探究一下其中的原理。vue
/** * @param {[Object]} obj 目标对象 * @param {[String]} prop 目标对象的属性 * @param {[String]} descriptor [属性描述符:数据描述符和存取描述符两种形式] */ var obj = {}; var descriptor = Object.create(null) // 没有继承的属性 // 默认没有enumerable,没有configurabel,没有writeable descriptor.value = 'static'; object.definePorperty(obj, 'key', descriptor); // 属性描述符 object.definePorperty(obj, 'key', { enumerable: false, // 定义对象的属性是不是可枚举 writeable: faslse, // 定义对象的属性是否可修改 configurable: fase, // 定义对象属性是否可删除 value: "static" }) // 存取描述符 var bValue object.defineProperty(obj, 'key', { get: function() { return bValue; }, set: function(newValue) { bValue = newValue }, eumerable: true, configurable: true })
vue在denfineProperty的方法上进行进一步的封装数组
function defineReactive(data,key,value) { Object.defineProerty(data,key,{ get: function() { return value }, set: function(newValue) { if(newValue === value) return; value = newValue; }, eumerable: true, writeable: true }) }
咱们之因此要观察Observer,是为了当数据属性发生变化的时候,通知那些使用到该属性的地方。
这要咱们就能够先把全部使用到该属性依赖所有收集起来,当属性改变的时候,循环通知全部的属性依赖进行更新框架
在defineReative的基础上进一步实现ui
/** * [defineReactive description] * @param {[Object]} data [对象obj] * @param {[String]} key [对象属性] * @param {[String]} value [对象属性的值] * @return {[type]} [description] */ function defineReactive(data,key,value) { let dep = []; Object.defineProperty(data,key,{ eumerable: true, configurable: true, get: function() { dep.push(watcher); }, set: function(newValue) { if(value === newValue) return; for(let i = 0; i < dep.length; i++ ) { watcher.update(newValue, value); } value = newValue } }) } // 进一步把收集依赖的部分封装起来 class Dep { static target: ?Watcher; id: Number; subs: String<watcher>; construstor() { this.id = uid++; this.subs = []; } addSub(sub: Watcher) { this.subs.push(sub) } removeSub(sub: Watcher) { remove(this.subs, sub) } depend() { if(Dep.target == Watcher) { this.addSub(Dep.target) } } notify() { const subs = this.subs.splice(); for(let i = 0; i < subs.length; i++) { subs[i].update(); } } } // 修改后的代码 function defineReactive(data,key,value) { let dep = New Dep(); Object.defineProperty(data,key, { eumerable: true, configaruable: true, get: function() { dep.depend(); return value; }, set: function(NewValue) { if(value === NewValue) return; dep.notify(); value = NewValue; } }) }
总结: 要观察就要收集依赖到Dep用于专门存储依赖, 上面咱们将全部存储到dep数组的叫作watcher。致这些watcher依赖是在vue中的templete,watch,computed中都要是收集的,当数据改变的时候就去通知到这些地方,这样就须要抽象出一个可以处理不一样状况的类,而后咱们在依赖收集的时候只须要收集这些封装好的类进来,通知也是通知到它一个,而后它负责通知其余地方使用到该属性的地方,这个类咱们叫它watcherthis
class Watcher { constructor(expOrFn, callback) { this.getter = parsePath(expOrFn); this.callback = callback; this.value = this.get(); } get() { // 执行get, 将本身注入到dep.target Dep.target = this; this.getter.call(vm); // 触发getter,getter里面触发depend, depend执行addSub,将watcher放到dep依赖中 Dep.tabget = undefined; } update() { const oldValue = this.value; this.value = this.get(); this.callback(this.value, oldValue) } }
上面的代码就是一个完整的一个watcher,所作的事情就是获取和更新要用到的属性code
至此,单个属性监测响应的整个过程就实现了,对于多个属性的监测代码实现以下,遍历对象的全部属性server
function observe(obj) { if(obj.constructor !== Object) return; const keys = obj.keys(); for(let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } function defineReactive(data,key,value) { observe(val); let dep = new Dep(); Object.defineProperty(data,key, { eumerable: true, configurable: true, get: function() { dep.depend(); return value; }, set: function(newValue) { if(value === newValue) return; dep.notify(); value = newValue; } }) }