摘要:調(diào)用父類的方法類在我們上一步已經(jīng)實(shí)現(xiàn)。我們先實(shí)現(xiàn)的綁定,因?yàn)槭且槐O(jiān)聽,所以要進(jìn)行進(jìn)一步的處理。調(diào)用父類的方法方法綁定完事,其實(shí)就這么簡單。
看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。
前言激動(dòng)人心的時(shí)候即將來臨,之前我們做的 8 步,其實(shí)都在為這一步打基礎(chǔ),這一步,我們來簡單實(shí)現(xiàn)一個(gè) Vue 對(duì)象,還沒有看過之前代碼的同學(xué),請(qǐng)確認(rèn)看過之前的文章。
主要實(shí)現(xiàn)內(nèi)容我們從測(cè)試代碼入手,來看我們這個(gè) Vue 實(shí)現(xiàn)了什么,然后在根據(jù)要實(shí)現(xiàn)的內(nèi)容來編寫這個(gè) Vue 對(duì)象:
let test = new Vue({ data() { return { baseTest: "baseTest", objTest: { stringA: "stringA", stringB: "stringB" } } }, methods: { methodTest() { console.log("methodTest") this.$emit("eventTest", "事件測(cè)試") } }, 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
主要實(shí)現(xiàn)的內(nèi)容有:
有屬性的監(jiān)聽 Watcher
實(shí)例下 data/methods 數(shù)據(jù)的代理(直接使用 this.xxx 就能訪問到具體的屬性/方法)
有事件 $on/$emit
實(shí)現(xiàn)我們根據(jù)實(shí)現(xiàn)的難易程度來實(shí)現(xiàn)上面 3 點(diǎn)。
實(shí)現(xiàn)第 3 點(diǎn),只要繼承 Event 這個(gè)類即可:
注: 在 Vue 源碼中并不是通過這個(gè)方式實(shí)現(xiàn)的事件,有興趣的可以自己去了解下,但是在我看來這樣是最容易理解的方式。
class Vue extends Event { constructor() { // 調(diào)用父類的 constructor 方法 super() ... } ... }
Event 類在我們上一步已經(jīng)實(shí)現(xiàn)。
接著我們來處理第二點(diǎn)。為了方便代碼的管理,我們?cè)陬愊露x一個(gè) _init 方法,來實(shí)現(xiàn) Vue 的初始化。
我們先實(shí)現(xiàn) methods 的綁定,因?yàn)?data 是要被監(jiān)聽,所以要進(jìn)行進(jìn)一步的處理。
class Vue extends Event { constructor(options) { // 調(diào)用父類的 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 方法綁定完事,其實(shí)就這么簡單。
接下來我們來處理 data ,由于 data 是需要被變換成可監(jiān)聽結(jié)構(gòu),所以我們先處理一下,然后代理到 this 對(duì)象下,如果直接賦值而不代理的話 data 的可監(jiān)聽結(jié)構(gòu)就會(huì)被破壞,我們需要一個(gè)完整的對(duì)象,這個(gè)可監(jiān)聽結(jié)構(gòu)才能完整。
這里先實(shí)現(xiàn)一下代理的方法:
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 方法來實(shí)現(xiàn),當(dāng)訪問(get) target 下的某個(gè)屬性的時(shí)候,就會(huì)去找 target[sourceKey] 下的同名屬性,設(shè)置(set) target 下的某個(gè)屬性,就會(huì)讓設(shè)置 target[sourceKey] 下的同名屬性。這就實(shí)現(xiàn)了代理。
ok 代理實(shí)現(xiàn),我們繼續(xù)為 _init 添加方法,具體的步驟看代碼中的注釋
class Vue extends Event { constructor(options) { // 調(diào)用父類的 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 是個(gè)函數(shù),所以需要調(diào)用,并綁定上下文環(huán)境 vm._data = options.data.call(vm) // 將 vm._data 變成可監(jiān)聽結(jié)構(gòu),實(shí)現(xiàn) watcher 的添加 observe(vm._data) // 代理屬性,這保證了監(jiān)聽結(jié)構(gòu)是一個(gè)完成的對(duì)象 for (let key in vm._data) { proxy(vm, "_data", key) } } }
最后一步,添加 watcher ,仔細(xì)分析我們?cè)趯?shí)例化時(shí)寫的 watcher:
watch: { "baseTest"(newValue, oldValue) { console.log(`baseTest change ${oldValue} => ${newValue}`) }, "objTest.stringA"(newValue, oldValue) { console.log(`objTest.stringA change ${oldValue} => ${newValue}`) } }
key 為需要監(jiān)聽的屬性的路徑,value 為觸發(fā)監(jiān)聽時(shí)的回調(diào)。
ok 我們來實(shí)現(xiàn)它
class Vue extends Event { constructor(options) { super() this._init(options) } _init(options) { ... // 循環(huán)取出 key/value for (let key in options.watch) { // 用我們之前實(shí)現(xiàn)的 Watcher 來注冊(cè)監(jiān)聽 // 參一:watcher 的運(yùn)行環(huán)境 // 參二:獲取注冊(cè)該 watcher 屬性 // 參三:觸發(fā)監(jiān)聽時(shí)的回調(diào) new Watcher(vm, () => { // 需要監(jiān)聽的值,eg: "objTest.stringA" ==> vm.objTest.stringA return key.split(".").reduce((obj, name) => obj[name], vm) }, options.watch[key]) } } }
ok watcher 也已經(jīng)實(shí)現(xiàn),以下就是完整的代碼:
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]) } } }
接下來,我們來測(cè)試一下
let test = new Vue({ data() { return { baseTest: "baseTest", objTest: { stringA: "stringA", stringB: "stringB" } } }, methods: { methodTest() { console.log("methodTest") this.$emit("eventTest", "事件測(cè)試") } }, 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 // 事件測(cè)試 test.baseTest = "baseTestChange" // baseTest change baseTest => baseTestChange test.objTest.stringA = "stringAChange" // objTest.stringA change stringA => stringAChange
剛開始使用 Vue 的時(shí)候,感覺代碼里面都是些黑魔法,在看了源碼之后驚覺:其實(shí) Vue 的整個(gè)實(shí)現(xiàn)并沒有什么黑魔法,有的是精心的結(jié)構(gòu)和處理,耐心點(diǎn)看下去,我相信我的收獲會(huì)很大。
點(diǎn)擊查看相關(guān)代碼
系列文章地址VUE - MVVM - part1 - defineProperty
VUE - MVVM - part2 - Dep
VUE - MVVM - part3 - Watcher
VUE - MVVM - part4 - 優(yōu)化Watcher
VUE - MVVM - part5 - Observe
VUE - MVVM - part6 - Array
VUE - MVVM - part7 - Event
VUE - MVVM - part8 - 優(yōu)化Event
VUE - MVVM - part9 - Vue
VUE - MVVM - part10 - Computed
VUE - MVVM - part11 - Extend
VUE - MVVM - part12 - props
VUE - MVVM - part13 - inject & 總結(jié)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94631.html
摘要:在中關(guān)于如何實(shí)現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。換個(gè)說法,當(dāng)我們?nèi)≈档臅r(shí)候,函數(shù)自動(dòng)幫我們添加了針對(duì)當(dāng)前值的依賴,當(dāng)這個(gè)值發(fā)生變化的時(shí)候,處理了這些依賴,比如說節(jié)點(diǎn)的變化。 在 VUE 中關(guān)于如何實(shí)現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。 首先,我們需要了解一下 js 中的一個(gè) API :Object.defineProperty(obj, prop,...
摘要:事件是什么在標(biāo)準(zhǔn)瀏覽器中,我們經(jīng)常使用來為一個(gè)添加一個(gè)事件等。仔細(xì)看這些情況,歸結(jié)到代碼中,無非就是一個(gè)行為或情況的名稱,和一些列的動(dòng)作,而在中動(dòng)作就是,一系列的動(dòng)作就是一個(gè)函數(shù)的集合。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 事件是什么? 在標(biāo)準(zhǔn)瀏覽器中,我們經(jīng)常使用:addEventListener 來為一個(gè) DOM 添加一個(gè)事件(click、mouse...
摘要:看這篇之前,如果沒看過先移步看實(shí)現(xiàn)中。同樣的,在取值時(shí)收集依賴,在設(shè)置值當(dāng)值發(fā)生變化時(shí)觸發(fā)依賴。中實(shí)現(xiàn)了一個(gè)的類來處理以上兩個(gè)問題,之后再說。以下語法下的,源碼中差不多就這樣點(diǎn)擊查看相關(guān)代碼系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒看過 step1 先移步看 實(shí)現(xiàn) VUE 中 MVVM - step1 - defineProperty。 在上一篇我們大概實(shí)現(xiàn)了,Vue 中的依賴收集和...
摘要:所以方法,是對(duì)默認(rèn)進(jìn)行擴(kuò)展,從而實(shí)現(xiàn)擴(kuò)展。這里我用了這個(gè)庫提供的合并方法,用來合并兩個(gè)對(duì)象,并不會(huì)修改原對(duì)象的內(nèi)容。測(cè)試符合我們的預(yù)期,方法也就實(shí)現(xiàn)了,下一步,實(shí)現(xiàn)父子組件。系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。 組件的擴(kuò)展 在 Vue 中有 extend 方法可以擴(kuò)展 Vue 的實(shí)例,在上一步中,有一些實(shí)現(xiàn)是必須要通過子父組件才...
摘要:通過裝作這些變化,我們實(shí)現(xiàn)了從而到達(dá)了數(shù)據(jù)變化觸發(fā)函數(shù)的過程。于此同時(shí),我們還實(shí)現(xiàn)了來擴(kuò)展這個(gè)可響應(yīng)的結(jié)構(gòu),讓這個(gè)對(duì)象擁有了觸發(fā)和響應(yīng)事件的能力。最后,根據(jù)我們的實(shí)現(xiàn),這是最終的產(chǎn)出,一個(gè)框架,了解一下系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。 provide / inject 在上一步我們實(shí)現(xiàn)了,父子組件,和 props 一樣 pr...
閱讀 1356·2023-04-26 00:35
閱讀 2728·2023-04-25 18:32
閱讀 3379·2021-11-24 11:14
閱讀 782·2021-11-22 15:24
閱讀 1432·2021-11-18 10:07
閱讀 6561·2021-09-22 10:57
閱讀 2787·2021-09-07 09:58
閱讀 3575·2019-08-30 15:54