看這篇以前,若是沒看過 step1
先移步看 實現 VUE 中 MVVM - step1 - defineProperty。vue
在上一篇咱們大概實現了,Vue
中的依賴收集和觸發,但咱們僅僅是將依賴維護在一個內置數組中,這樣作雖然容易理解,但畢竟很差維護,爲了更容易的維護這些依賴,咱們來實現一個維護依賴的類。git
首先咱們能夠先肯定這個類下的屬性,以及一些功能:github
類下屬性:數組
實例下屬性及方法:瀏覽器
考慮到直接放在瀏覽器上執行,因此直接用 ES5
的類寫法。函數
let Dep = function(){ // 實例屬性 this.subs = [] // 實例方法 this.addSub = function(sub){ this.subs.push(sub) } this.removeSub = function(sub){ const index = this.subs.indexOf(item) if (index > -1) { this.subs.splice(index, 1) } } this.notify = function(newValue, oldVaule){ this.subs.forEach(fnc=>fnc(newValue, oldVaule)) } } // 類屬性 Dep.target = null
好了,如今咱們擁有了一個管理依賴的類(這裏將依賴簡化爲一個方法),如今咱們就能夠動手來改一下以前的代碼了。優化
let defineReactive = function(object, key, value){ let dep = new Dep() Object.defineProperty(object, key, { configurable: true, enumerable: true, get: function(){ if(Dep.target){ dep.addSub(Dep.target) } return value }, set: function(newValue){ if(newValue != value){ dep.notify(newValue, value) } value = newValue } }) }
能夠發現,以前咱們用來存放依賴的數組變成了一個依賴管理(Dep
)的實例。一樣的,在取值時收集依賴,在設置值(當值發生變化)時觸發依賴。this
因爲依賴的處理由 Dep
的實例管理了,這裏僅僅調用一下相關方法便可。code
接下來試一試效果:blog
let object = {} defineReactive(object, 'test', 'test') Dep.target = function(newValue, oldValue){ console.log('我被添加進去了,新的值是:' + newValue) } object.test // test Dep.target = null object.test = 'test2' // 我被添加進去了,新的值是:test2 Dep.target = function(newValue, oldValue){ console.log('添加第二個函數,新的值是:' + newValue) } object.test // test Dep.target = null object.test = 'test3' // 我被添加進去了,新的值是:test3 // 添加第二個函數,新的值是:test3
可是上面的代碼暴露了幾個問題
Dep
這個類將監聽屬性和處理依賴進行了解耦,可是卻沒有徹底解耦,在觸發依賴的時候,仍是得傳新舊值。Dep
中定義的 removeSub
在代碼中並無用到,由於 Dep
的實例是在 defineReactive
函數的做用域中,外部並不能直接調用,而刪除依賴確定是在外部的環境中,也就是說即便咱們將代碼改爲這樣,咱們仍是不能直接取刪除已經沒用的依賴。Vue
中實現了一個 Watcher
的類來處理以上兩個問題,以後再說。
如下 ES6
語法下的 Dep
,Vue
源碼中差很少就這樣
class Dep { constructor() { this.subs = [] } addSub(sub) { this.subs.push(sub) } removeSub(sub) { const index = this.subs.indexOf(item) if (index > -1) { this.subs.splice(index, 1) } } notify() { this.subs.forEach(fnc=>fnc(oldValue, newValue)) } } Dep.target = null