VUE - MVVM - part5 - Observe

看這篇之前,假如沒有看過之前的文章,可拉到文章末端檢察之前的文章。

回憶

step4 中,我們大抵完成了一個 MVVM 的框架,由3個部份構成:

  1. defineReactive 掌握了對象屬性,使變成可監聽構造
  2. Dep 網絡治理依靠
  3. Watcher 一個籠統的依靠

defineReactiveDep 革新了對象下的某個屬性,將目的變成了觀察者形式中的目的,當目的發生變化時,會挪用觀察者;

Watcher 就是一個詳細的觀察者,會註冊到目的中。

之前的代碼完成了觀察者形式,使得數據的變化得以相應,然則照樣有兩個須要優化的處所:

  1. 假如我們想讓對象的屬性都得以相應,那我們必需對對象下的一切屬性舉行遍歷,順次挪用 defineReactive 這不是很輕易
  2. 代碼都在一個文件中,不利於治理

處理

題目2

先處理第二個題目,我們僅僅須要把代碼舉行分別即可,然後用 webpack/babel 打包即可,固然這裏就不說怎樣去設置 webpack ,運用 webpack/babel 我們就能夠運用 ES6 的語法和模塊體系了。

然則為了偷懶,我把代碼直接放在 node 環境中實行了,然則 import 語法須要特定的 node 版本,我這裏運用的是 8.11.1(版本網上都應該是支撐的),同時須要特定的文件後綴(.mjs)和敕令 node --experimental-modules xxx.mjs

詳細代碼

實行體式格局進入到 step5 的目次下,敕令行運轉 node --experimental-modules test.mjs 即可。

固然你也能夠用 webpack/babel 舉行打包和轉碼,然後放到瀏覽器上運轉即可。

題目1

關於題目1,我們須要做的僅僅是完成一個要領舉行遍歷對象屬性即可。我們把這個歷程籠統成一個對象 Observe 。至於為何要把這個歷程籠統成一個對象,背面會說。

注: 由因而在 node 環境下運轉代碼,這裏就直接用 ES6 的語法了。一樣的我把別的模塊也用 ES6 語法寫了一遍。

export class Observer {

    constructor(value) {
        this.value = value
        this.walk(value)
        // 標誌這個對象已被遍歷過,同時保留 Observer
        Object.defineProperty(value, '__ob__', {
            value: this,
            enumerable: false,
            writable: true,
            configurable: true
        })
    }

    walk(obj) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i], obj[keys[i]])
        }
    }
}

從代碼能夠看出,這個類在實例化的時刻自動遍歷了傳入參數下的一切屬性(value),並把每一個屬性都應用了 defineReactive
為了確保傳入的值為對象,我們再寫一個要領來推斷。

export function observe (value) {
    // 確保 observe 為一個對象
    if (typeof value !== 'object') {
        return
    }
    let ob
    // 假如對象下有 Observer 則不須要再次天生 Observer
    if (value.hasOwnProperty('__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
    } else if (Object.isExtensible(value)) {
        ob = new Observer(value)
    }
    return ob
}

函數返回該對象的 Observer 實例,這裏推斷了假如該對象下已有 Observer 實例,則直接返回,不再去臨盆 Observer 實例。這就確保了一個對象下的 Observer 實例僅被實例化一次。

上面代碼完成了對某個對象下一切屬性的轉化,然則假如對象下的某個屬性是對象呢?
所以我們還需革新一下 defineReactive 詳細代碼為:

export function defineReactive(object, key, value) {
    let dep = new Dep()
    // 遍歷 value 下的屬性,因為在 observe 中已推斷是不是為對象,這裏就不推斷了
    observe(value)
    Object.defineProperty(object, key, {
        configurable: true,
        enumerable: true,
        get: function () {
            if (Dep.target) {
                dep.addSub(Dep.target)
                Dep.target.addDep(dep)
            }
            return value
        },
        set: function (newValue) {
            if (newValue !== value) {
                value = newValue
                dep.notify()
            }
        }
    })
}

ok 我們來測試下

import Watcher from './Watcher'
import {observe} from "./Observe"

let object = {
    num1: 1,
    num2: 1,
    objectTest: {
        num3: 1
    }
}

observe(object)

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

object.num1 = 2
// 監聽函數,2 + 1 + 1 = 4
object.objectTest.num3 = 2
// 監聽函數,2 + 1 + 2 = 5

固然為了更好的相識這個歷程,最好把 step5 目次中的代碼拉下來一同看。至於之前完成的功用這裏就不特地寫測試了。

末了

末了詮釋下為何要把遍歷對象屬性這個歷程籠統成一個對象

  • 對象在 js 下寄存是是援用,也就是說有能夠幾個對象下的某個屬性是同一個對象下的援用,以下

    let obj1 = {num1: 1}
    let obj2 = {obj: obj1}
    let obj3 = {obj: obj1}

    假如我們籠統成對象,而僅僅是函數挪用的話,那末 obj1 這個對象就會遍歷兩次,而籠統成一個對象的話,我們能夠把這個對象保留在 obj1 下(__ob__ 屬性),遍歷的時刻推斷一下就好。

  • 固然處理上面題目我們也能夠在 obj1 下設置一個標誌位即可,然則這個對象在以後會有特別的用處,先如許寫吧。(與數組和 Vue.set 有關)

在代碼中我為 DepWatch 添加了 id 這個臨時用不到,先加上。

點擊檢察相干代碼

系列文章地點

  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 & 總結
    原文作者:aco
    原文地址: https://segmentfault.com/a/1190000014412475
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞