看這篇以前,若是沒有看過以前的文章,可拉到文章末尾查看以前的文章。vue
在 step2
中,咱們實現了一個管理依賴的 Dep
,可是僅僅使用這個類並不能完成咱們想實現的功能,並且代碼的解耦上也有點小問題。如下是在 step2
中最後說的幾個問題:git
第一個問題顯示出來一個問題,因爲咱們的依賴是函數,爲了函數的執行咱們只能講參數傳進去,這個問題的根源在於咱們的依賴是一個函數;github
第二個問題其實反映出當前的 dep
實例只有在 defineReactive
中使用,而沒有暴露出來,只要在外部有這個實例的引用,那麼咱們就能順利的調用移除依賴了(removeSub
)。數組
解決第一個問題很簡單,咱們把某個屬性的值、對應值變化時須要執行的函數抽象成一個對象,而後把這個對象當成是依賴,推入依賴管理中。函數
在第一個問題的基礎上第二個問題就能解決了,咱們只須要把 dep
的引用保存在依賴對象中就能夠了。測試
固然我也是在看了 Vue
源碼的基礎上纔有了上面的解決辦法,這裏不得不給尤大大讚一個。優化
有了以上的考慮,那個依賴對象在 Vue
中就是 Watcher
。this
let Watcher = function(object, key, callback){ this.obj = object this.getter = key this.cb = callback this.dep = null this.value = undefined this.get = function(){ Dep.target = this let value = this.obj[this.getter] Dep.target = null return value } this.update = function(){ const value = this.obj[this.getter] const oldValue = this.value this.value = value this.cb.call(this.obj, value, oldValue) } this.addDep = function(dep) { this.dep = dep } this.value = this.get() }
上述代碼實現了一個 Watcher
,爲了方便起見我這裏叫它監聽。code
該類的實例保存了須要監聽的對象(object
),取值方法(key
),對應的回調(callback
),須要監聽的值(value
),以及取值函數(get
)和觸發函數(update
),這樣咱們就把依賴相關的全部內容保存在這個 Watcher
的實例中。對象
爲了保存對 Dep
的引用,在 Watcher
中設置了 dep
,用於存放該監聽被那個 Dep
給引用了。
因爲在 Watcher
實例化的時候,咱們已經對相應的值取了一次值,就是將如下代碼放在在 Watcher
中
Dep.target = function(newValue, oldValue){ console.log('我被添加進去了,新的值是:' + newValue) } object.test Dep.target = null
對應的代碼爲
this.get = function(){ Dep.target = this let vaule = this.obj[this.getter] Dep.target = null return value } this.value = this.get()
因此在編寫代碼的時候,不用特意的去觸發 get
添加依賴。
那麼針對 Watcher
咱們須要改造一下以前實現的 Dep
和 defineReactive
函數。
Watcher
因此在 Dep
中 notify
應該改爲 Watcher
下的觸發函數:update
watcher
中存放了變量的狀態,因此不須要在 defineReactive
函數中傳入參數let Dep = function(){ this.subs = [] this.addSub = function(sub){ this.subs.push(sub) } this.removeSub = function(sub){ const index = this.subs.indexOf(sub) if (index > -1) { this.subs.splice(index, 1) } } this.notify = function(){ // 修改觸發方法 this.subs.forEach(watcher=>watcher.update()) } } 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) // 添加 watcher 對 dep 的引用 Dep.target.addDep(dep) } return value }, set: function(newValue){ if(newValue != value){ value = newValue // 不須要特意傳入參數 dep.notify() } } }) }
接下來咱們來測試一下
let object = {} defineReactive(object, 'test', 'test') let watcher = new Watcher(object, 'test', function(newValue, oldValue){ console.log('做爲 watcher 添加的第一個函數,很自豪。新值:' + newValue) }) object.test = 'test2' // 做爲 watcher 添加的第一個函數,很自豪。新值:test2 let watcher2 = new Watcher(object, 'test', function(newValue, oldValue){ console.log('做爲 watcher 添加的第二個函數,也很自豪。新值:' + newValue) }) object.test = 'test3' // 做爲 watcher 添加的第一個函數,很自豪。新值:test3 // 做爲 watcher 添加的第二個函數,也很自豪。新值:test3 // 接着咱們來試一下刪除依賴,把 watcher2 給刪除 watcher2.dep.removeSub(watcher2) object.test = 'test4' // 做爲 watcher 添加的第一個函數,很自豪。新值:test4
經過上面代碼,咱們成功解耦,用一個監聽來處理某個屬性的內容(oldValue
, newValue
, callback
),並且咱們也可以去除 dep
中沒用的依賴。
固然這個 Watcher 仍是須要優化的,好比被多個 Dep
引用,這個就得存一個數組,以後繼續優化。