VUE - MVVM - part4 - 優化Watcher

看這篇以前,若是沒有看過以前的文章,可拉到文章末尾查看以前的文章。vue

回顧

首先咱們思考一下截止當前,咱們都作了什麼git

  1. 經過 defineReactive 這個函數,實現了對於數據取值和設置的監聽
  2. 經過 Dep 類,實現了依賴的管理
  3. 經過 Watcher 類,抽象出了對象下某個屬性的依賴,以及屬性變換的 callBack

發現問題

對比 VueMVVM(先把視圖層的渲染抽象成一個函數),咱們僅僅是實現了一些基礎性的東西。還有很大的區別,好比github

  1. 咱們的 Watcher 僅僅是抽象了對象下的單一屬性,而通常視圖層的渲染是涉及多個屬性的,而這些屬性的變化是同一個渲染函數(也就是 Vue 中編譯模板字符串最終生成的函數)。
  2. 經過第一點,咱們能夠得知,對象下的某幾個屬性是擁有同一個 Watcher 的,換句話說就是,多個 Dep 依賴與同一個 Watcher,那麼 Watcher 中該如何保存這些 Dep ,由於按照咱們的實現,都一個 Watcher 中僅僅保持一個 Dep

解決問題

問題1

先讓咱們想一想,咱們是如何把依賴注入到 Dep 中的數組

經過取值觸發 defineProperty 中的 get,而後添加依賴函數

換句話說就是,我只要取過對應屬性的值,那麼就能夠添加依賴。
看到以前 Watcher 的實現:測試

this.get = function () {
    Dep.target = this
    let value = this.getter.call(object)
    Dep.target = null
    return value
}

這段代碼就實現了添加相應屬性的依賴,歸根究竟是這段起了做用優化

let value = this.obj[this.getter]

這裏觸發了對應屬性的 get ,那好針對第一個問題,咱們只要在這裏觸發多個屬性的 get 便可,至於要觸發那些屬性,咱們交由調用者來控制,瓜熟蒂落的這裏應該是一個函數。考慮以後便有了如下代碼this

let Watcher = function (object, getter, callback) {
    this.obj = object
    // 這裏的 getter 應該是一個函數
    this.getter = getter
    this.cb = callback
    this.dep = null
    this.value = undefined

    this.get = function () {
        Dep.target = this
        // 將取值方式改爲函數調用
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

    this.update = function () {
        const value = this.getter.call(object)
        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()
}

問題二

問題二其實很簡單,既然要保存多個 dep 咱們把保存的值聲明成一個數組便可code

let Watcher = function (object, getter, callback) {
    this.obj = object
    this.getter = getter
    this.cb = callback
    // 聲明成數組
    this.deps = []
    this.value = undefined

    this.get = function () {
        Dep.target = this
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

    this.update = function () {
        const value = this.getter.call(object)
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }

    this.addDep = function (dep) {
        // 將 dep 推入數組中
        this.deps.push(dep)
    }

    this.value = this.get()
}

爲了方便取消這個 Watcher ,咱們在添加一個函數,用於取消全部 DepWatcher 的依賴,因此最終 Watcher 的代碼以下:對象

let Watcher = function (object, getter, callback) {
    this.obj = object
    this.getter = getter
    this.cb = callback
    this.deps = []
    this.value = undefined

    this.get = function () {
        Dep.target = this
        let value = this.getter.call(object)
        Dep.target = null
        return value
    }

    this.update = function () {
        const value = this.getter.call(object)
        const oldValue = this.value
        this.value = value
        this.cb.call(this.obj, value, oldValue)
    }

    this.addDep = function (dep) {
        this.deps.push(dep)
    }

    // 新添加的取消依賴的方法
    this.teardown = function () {
        let i = this.deps.length
        while (i--) {
            this.deps[i].removeSub(this)
        }
        this.deps = []
    }

    this.value = this.get()
}

測試

咱們僅僅優化了 Watcher 的實現,其餘的代碼並無發生變化

let object = {}
defineReactive(object, 'num1', 2)
defineReactive(object, 'num2', 4)

let watcher = new Watcher(object, function () {
    return this.num1 + this.num2
}, function (newValue, oldValue) {
    console.log(`這是一個監聽函數,${object.num1} + ${object.num2} = ${newValue}`)
})

object.num1 = 3
// 這是一個監聽函數,3 + 4 = 7
object.num2 = 10
// 這是一個監聽函數,3 + 10 = 13

let watcher2 = new Watcher(object, function () {
    return this.num1 * this.num2
}, function (newValue, oldValue) {
    console.log(`這是一個監聽函數,${object.num1} * ${object.num2} = ${newValue}`)
})

object.num1 = 4
// 這是一個監聽函數,4 + 10 = 14
// 這是一個監聽函數,4 * 10 = 40
object.num2 = 11
// 這是一個監聽函數,4 + 11 = 15
// 這是一個監聽函數,4 * 11 = 44

// 測試取消
watcher2.teardown()

object.num1 = 5
// 這是一個監聽函數,5 + 11 = 16
object.num2 = 12
// 這是一個監聽函數,5 + 12 = 17

這就實現了對於多個屬性設置同一個監聽,當監聽函數中的依賴屬性發生變化時,自動執行了相應的函數。

關於 Vue 中的 MVVM 的實現 ,差很少也就這樣了,固然這僅僅是基礎的實現,並且視圖層層渲染抽象成一個函數。

不一樣於 Vue 中的實現,這裏少了不少各類標記和應用標記的過程。

這些會增長理解難度,以後有用到再說,實現完整的 MVVM 還須要對數組進行特殊的處理,由於數組是不能用 Object.defineProperty 來處理索引值的,這個也以後再說。

點擊查看相關代碼

系列文章地址

  1. VUE - MVVM - part1 - defineProperty
  2. VUE - MVVM - part2 - Dep
  3. VUE - MVVM - part3 - Watcher
  4. VUE - MVVM - part4 - 優化Watcher
  5. VUE - MVVM - part5 - Observe
  6. VUE - MVVM - part6 - Array
  7. VUE - MVVM - part7 - Event
  8. VUE - MVVM - part8 - 優化Event
  9. VUE - MVVM - part9 - Vue
  10. VUE - MVVM - part10 - Computed
  11. VUE - MVVM - part11 - Extend
  12. VUE - MVVM - part12 - props
  13. VUE - MVVM - part13 - inject & 總結
相關文章
相關標籤/搜索