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; } }) }