VUE - MVVM - part9 - Vue

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

媒介

激動人心的時刻即將來臨,之前我們做的 8 步,實在都在為這一步打基礎,這一步,我們來簡樸完成一個 Vue 對象,還沒有看過之前代碼的同硯,請確認看過之前的文章。

重要完成內容

我們從測試代碼入手,來看我們這個 Vue 完成了什麼,然後在依據要完成的內容來編寫這個 Vue 對象:

let test = new Vue({
    data() {
        return {
            baseTest: 'baseTest',
            objTest: {
                stringA: 'stringA',
                stringB: 'stringB'
            }
        }
    },
    methods: {
        methodTest() {
            console.log('methodTest')
            this.$emit('eventTest', '事宜測試')
        }
    },
    watch: {
        'baseTest'(newValue, oldValue) {
            console.log(`baseTest change ${oldValue} => ${newValue}`)
        },
        'objTest.stringA'(newValue, oldValue) {
            console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
        }
    }
})

test.$on('eventTest', function (event) {
    console.log(event)
})

test.methodTest()

test.baseTest

重要完成的內容有:

  1. 有屬性的監聽 Watcher
  2. 實例下 data/methods 數據的代辦(直接運用 this.xxx 就能夠接見到詳細的屬性/要領)
  3. 有事宜 $on/$emit

完成

我們依據完成的難易水平來完成上面 3 點。

完成第 3 點,只需繼承 Event 這個類即可:

注:Vue 源碼中並非經由過程這個體式格局完成的事宜,有興緻的能夠本身去了解下,然則在我看來如許是最輕易明白的體式格局。

class Vue extends Event {
    constructor() {
        // 挪用父類的 constructor 要領
        super()
        ...
    }
    ...
}

Event 類在我們上一步已完成。

接着我們來處置懲罰第二點。為了輕易代碼的治理,我們在類下定義一個 _init 要領,來完成 Vue 的初始化。

我們先完成 methods 的綁定,因為 data 是要被監聽,所以要舉行進一步的處置懲罰。

class Vue extends Event {
    constructor(options) {
        // 挪用父類的 constructor 要領
        super()
        this._init(options)
    }
    
    _init(options) {
        let vm = this
        if (options.methods) {
            for (let key in options.methods) {
                vm[key] = options.methods[key].bind(vm)
            }
        }
    }
}

ok methods 要領綁定完事,實在就這麼簡樸。

接下來我們來處置懲罰 data ,因為 data 是須要被變換成可監聽構造,所以我們先處置懲罰一下,然後代辦到 this 對象下,假如直接賦值而不代辦的話 data 的可監聽構造就會被損壞,我們須要一個完全的對象,這個可監聽構造才完全。

這裏先完成一下代辦的要領:

export function proxy(target, sourceKey, key) {
    const sharedPropertyDefinition = {
        enumerable: true,
        configurable: true,
        get() {
        },
        set() {
        }
    }
    sharedPropertyDefinition.get = function proxyGetter() {
        return this[sourceKey][key]
    }
    sharedPropertyDefinition.set = function proxySetter(val) {
        this[sourceKey][key] = val
    }
    Object.defineProperty(target, key, sharedPropertyDefinition)
}

道理照樣經由過程 Object.defineProperty 要領來完成,當接見(gettarget 下的某個屬性的時刻,就會去找 target[sourceKey] 下的同名屬性,設置(settarget 下的某個屬性,就會讓設置 target[sourceKey] 下的同名屬性。這就完成了代辦。

ok 代辦完成,我們繼承為 _init 增加要領,詳細的步驟看代碼中的解釋

class Vue extends Event {
    constructor(options) {
        // 挪用父類的 constructor 要領
        super()
        this._init(options)
    }
    
    _init(options) {
        let vm = this
        if (options.methods) {
            for (let key in options.methods) {
                // 綁定 this 指向
                vm[key] = options.methods[key].bind(vm)
            }
        }
        // 因為 data 是個函數,所以須要挪用,並綁定上下文環境
        vm._data = options.data.call(vm)
        // 將 vm._data 變成可監聽構造,完成 watcher 的增加
        observe(vm._data)
        // 代辦屬性,這保證了監聽構造是一個完成的對象
        for (let key in vm._data) {
            proxy(vm, '_data', key)
        }
    }
}

末了一步,增加 watcher ,仔細分析我們在實例化時寫的 watcher

watch: {
    'baseTest'(newValue, oldValue) {
        console.log(`baseTest change ${oldValue} => ${newValue}`)
    },
    'objTest.stringA'(newValue, oldValue) {
        console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
    }
}

key 為須要監聽的屬性的途徑,value 為觸發監聽時的回調。

ok 我們來完成它

class Vue extends Event {
    constructor(options) {
        super()
        this._init(options)
    }

    _init(options) {
        ...

        // 輪迴掏出 key/value
        for (let key in options.watch) {
            // 用我們之前完成的 Watcher 來註冊監聽
            // 參一:watcher 的運轉環境
            // 參二:獵取註冊該 watcher 屬性
            // 參三:觸發監聽時的回調 
            new Watcher(vm, () => {
                // 須要監聽的值,eg: 'objTest.stringA' ==> vm.objTest.stringA
                return key.split('.').reduce((obj, name) => obj[name], vm)
            }, options.watch[key])
        }

    }
}

ok watcher 也已完成,以下就是完全的代碼:

export function proxy(target, sourceKey, key) {
    const sharedPropertyDefinition = {
        enumerable: true,
        configurable: true,
        get() {
        },
        set() {
        }
    }
    sharedPropertyDefinition.get = function proxyGetter() {
        return this[sourceKey][key]
    }
    sharedPropertyDefinition.set = function proxySetter(val) {
        this[sourceKey][key] = val
    }
    Object.defineProperty(target, key, sharedPropertyDefinition)
}

let uid = 0

export class Vue extends Event {
    constructor(options) {
        super()
        this._init(options)
    }

    _init(options) {
        let vm = this
        vm.uid = uid++

        if (options.methods) {
            for (let key in options.methods) {
                vm[key] = options.methods[key].bind(vm)
            }
        }

        vm._data = options.data.call(vm)
        observe(vm._data)
        for (let key in vm._data) {
            proxy(vm, '_data', key)
        }

        for (let key in options.watch) {
            new Watcher(vm, () => {
                return key.split('.').reduce((obj, name) => obj[name], vm)
            }, options.watch[key])
        }

    }
}

接下來,我們來測試一下

let test = new Vue({
    data() {
        return {
            baseTest: 'baseTest',
            objTest: {
                stringA: 'stringA',
                stringB: 'stringB'
            }
        }
    },
    methods: {
        methodTest() {
            console.log('methodTest')
            this.$emit('eventTest', '事宜測試')
        }
    },
    watch: {
        'baseTest'(newValue, oldValue) {
            console.log(`baseTest change ${oldValue} => ${newValue}`)
        },
        'objTest.stringA'(newValue, oldValue) {
            console.log(`objTest.stringA change ${oldValue} => ${newValue}`)
        }
    }
})

test.$on('eventTest', function (event) {
    console.log(event)
})

test.methodTest()
// methodTest
// 事宜測試

test.baseTest = 'baseTestChange'
// baseTest change baseTest => baseTestChange

test.objTest.stringA = 'stringAChange'
// objTest.stringA change stringA => stringAChange

剛開始運用 Vue 的時刻,覺得代碼內里都是些黑魔法,在看了源碼以後驚覺:實在 Vue 的全部完成並沒有什麼黑魔法,有的是經心的構造和處置懲罰,耐煩點看下去,我置信我的收成會很大。

點擊檢察相干代碼

系列文章地點

  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/1190000014604971
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞