摘要:回顧在前面的幾個中,我們實現(xiàn)對象的屬性的監(jiān)聽,但是有關(guān)于數(shù)組的行為我們一直沒有處理。并且上述的幾個數(shù)組方法是數(shù)組對象提供的,我們要想辦法去觸發(fā)下的函數(shù)。在設(shè)置值的時候就能成功觸發(fā)依賴。
看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。
回顧在前面的幾個 step 中,我們實現(xiàn)對象的屬性的監(jiān)聽,但是有關(guān)于數(shù)組的行為我們一直沒有處理。
我們先分析下導致數(shù)組有哪些行為:
調(diào)用方法:arr.splice(1, 2, "something1", "someting2")
直接賦值:arr[1] = "something"
解決行為一首先我們知道數(shù)組下的一些方法是會對原數(shù)組照成影響的,有以下幾個:
push
pop
shift
unshift
splice
sort
reverse
這幾個方法總的來說會照成幾個影響:
數(shù)組長度發(fā)生變化
數(shù)組內(nèi)元素順序發(fā)生變化
不像對象,如果對象的 key 值的順序發(fā)生變化,是不會影響視圖的變化,但數(shù)組的順序如果發(fā)生變化,視圖是要變化的。
也就是說當著幾個方法觸發(fā)的時候,我們需要視圖的更新,也就是要觸發(fā) Dep 中的 notify 函數(shù)。
但是縱觀我們現(xiàn)在實現(xiàn)的代碼( step5 中的代碼),我們并沒有特地的為數(shù)組提供一個 Dep。
并且上述的幾個數(shù)組方法是數(shù)組對象提供的,我們要想辦法去觸發(fā) Dep 下的 notify 函數(shù)。
我們先為數(shù)組提供一個 Dep ,完善后的 Observer :
export class Observer { constructor(value) { this.value = value if (Array.isArray(value)) { // 為數(shù)組設(shè)置一個特殊的 Dep this.dep = new Dep() this.observeArray(value) } else { this.walk(value) } Object.defineProperty(value, "__ob__", { value: this, enumerable: false, writable: true, configurable: true }) } /** * 遍歷對象下屬性,使得屬性變成可監(jiān)聽的結(jié)構(gòu) */ walk(obj) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** * 同上,遍歷數(shù)組 */ observeArray (items) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
同樣的在 defineReactive 我們需要處理數(shù)組添加依賴的邏輯
export function defineReactive(object, key, value) { let dep = new Dep() let childOb = observe(value) Object.defineProperty(object, key, { configurable: true, enumerable: true, get: function () { if (Dep.target) { dep.addSub(Dep.target) Dep.target.addDep(dep) // 處理數(shù)組的依賴 if(Array.isArray(value)){ childOb.dep.addSub(Dep.target) Dep.target.addDep(childOb.dep) } } return value }, set: function (newValue) { if (newValue !== value) { value = newValue dep.notify() } } }) }
ok 我們現(xiàn)在完成了依賴的添加,剩下的我們要實現(xiàn)依賴的觸發(fā)。
處理方法:在數(shù)組對象調(diào)用特定方法時,首先找到的應(yīng)該是我們自己寫的方法,而這個方法中調(diào)用了原始方法,并觸發(fā)依賴。
我們先來包裝一下方法,得到一些同名方法:
const arrayProto = Array.prototype // 復制方法 export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ "push", "pop", "shift", "unshift", "splice", "sort", "reverse" ] /** * 改變數(shù)組的默認處理,將新添加的對象添加監(jiān)聽 */ methodsToPatch.forEach(function (method) { // 原始的數(shù)組處理方法 const original = arrayProto[method] let mutator = function (...args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case "push": case "unshift": inserted = args break case "splice": inserted = args.slice(2) break } // 新添加的對象需要添加監(jiān)聽 if (inserted) ob.observeArray(inserted) // 觸發(fā) notify 方法 ob.dep.notify() return result } Object.defineProperty(arrayMethods, method, { value: mutator, enumerable: false, writable: true, configurable: true }) })
ok 我們現(xiàn)在得到了一些列同名的方法,我只要確保在調(diào)用時,先調(diào)用到我們的方法即可。
有兩種方式可以實現(xiàn):
數(shù)組對象上直接有該方法,這樣就不會去找對象上的原型鏈
覆蓋對象的 __proto__ ,這樣尋找原型鏈時,就會先找到我們的方法
具體到代碼中的實現(xiàn):
export class Observer { constructor(value) { this.value = value if (Array.isArray(value)) { this.dep = new Dep() const augment = ("__proto__" in {}) ? protoAugment : copyAugment // 覆蓋數(shù)組中一些改變了原數(shù)組的方法,使得方法得以監(jiān)聽 augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) } ... } ... } /** * 如果能使用 __proto__ 則將數(shù)組的處理方法進行替換 */ function protoAugment (target, src, keys) { target.__proto__ = src } /** * 如果不能使用 __proto__ 則直接將該方法定義在當前對象下 */ function copyAugment (target, src, keys) { for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] Object.defineProperty(target, key, { value: src[key], enumerable: false, writable: true, configurable: true }) } }
測試一下:
let object = { arrayTest: [1, 2, 3, 4, 5] } observe(object) let watcher = new Watcher(object, function () { return this.arrayTest.reduce((sum, num) => sum + num) }, function (newValue, oldValue) { console.log(`監(jiān)聽函數(shù),數(shù)組內(nèi)所有元素 = ${newValue}`) }) object.arrayTest.push(10) // 監(jiān)聽函數(shù),數(shù)組內(nèi)所有元素 = 25
到現(xiàn)在為止,我們成功的在數(shù)組調(diào)用方法的時候,添加并觸發(fā)了依賴。
解決行為二首先先說明,數(shù)組下的索引是和對象下的鍵有同樣的表現(xiàn),也就是可以用 defineReactive 來處理索引值,但是數(shù)組是用來存放一系列的值,我們并不能一開始就確定數(shù)組的長度,并且極有可能剛開始數(shù)組長度為 0,之后數(shù)組中的索引對應(yīng)的內(nèi)容也會不斷的變化,所以為索引調(diào)用 defineReactive 是不切實際的。
但是類似于 arr[1] = "something" 這樣的賦值在數(shù)組中也是常見的操作,在 Vue 中實現(xiàn)了 $set 具體的細節(jié)這里不談,這里實現(xiàn)了另一種方法,我們僅僅需要在數(shù)組對象下添加一個方法即可:
arrayMethods.$apply = function () { this.__ob__.observeArray(this) this.__ob__.dep.notify() }
測試一下:
object.arrayTest[1] = 10 object.arrayTest.$apply() // 監(jiān)聽函數(shù),數(shù)組內(nèi)所有元素 = 33
到目前為了,一個完整的數(shù)據(jù)監(jiān)聽的模型也就完成了,我們可以使用 observe 方法來得到一個可監(jiān)聽結(jié)構(gòu),然后用 Watcher 添加依賴。
在設(shè)置值的時候就能成功觸發(fā)依賴。
點擊查看相關(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)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94449.html
摘要:在中關(guān)于如何實現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。換個說法,當我們?nèi)≈档臅r候,函數(shù)自動幫我們添加了針對當前值的依賴,當這個值發(fā)生變化的時候,處理了這些依賴,比如說節(jié)點的變化。 在 VUE 中關(guān)于如何實現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。 首先,我們需要了解一下 js 中的一個 API :Object.defineProperty(obj, prop,...
摘要:事件是什么在標準瀏覽器中,我們經(jīng)常使用來為一個添加一個事件等。仔細看這些情況,歸結(jié)到代碼中,無非就是一個行為或情況的名稱,和一些列的動作,而在中動作就是,一系列的動作就是一個函數(shù)的集合。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 事件是什么? 在標準瀏覽器中,我們經(jīng)常使用:addEventListener 來為一個 DOM 添加一個事件(click、mouse...
摘要:看這篇之前,如果沒看過先移步看實現(xiàn)中。同樣的,在取值時收集依賴,在設(shè)置值當值發(fā)生變化時觸發(fā)依賴。中實現(xiàn)了一個的類來處理以上兩個問題,之后再說。以下語法下的,源碼中差不多就這樣點擊查看相關(guān)代碼系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒看過 step1 先移步看 實現(xiàn) VUE 中 MVVM - step1 - defineProperty。 在上一篇我們大概實現(xiàn)了,Vue 中的依賴收集和...
摘要:所以方法,是對默認進行擴展,從而實現(xiàn)擴展。這里我用了這個庫提供的合并方法,用來合并兩個對象,并不會修改原對象的內(nèi)容。測試符合我們的預期,方法也就實現(xiàn)了,下一步,實現(xiàn)父子組件。系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。 組件的擴展 在 Vue 中有 extend 方法可以擴展 Vue 的實例,在上一步中,有一些實現(xiàn)是必須要通過子父組件才...
摘要:看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。回顧在上一步我們實現(xiàn)了一個簡易的事件管理的類,接下來我們把它給優(yōu)化下,方便我們的使用。接著我們來優(yōu)化。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 回顧 在上一步我們實現(xiàn)了一個簡易的事件管理的類,接下來我們把它給優(yōu)化下,方便我們的使用。主要優(yōu)化內(nèi)容: 方便為多個事件添加同一個函數(shù) 方便為一個事件...
閱讀 1236·2021-11-11 16:54
閱讀 1749·2021-10-13 09:40
閱讀 946·2021-10-08 10:05
閱讀 3511·2021-09-22 15:50
閱讀 3714·2021-09-22 15:41
閱讀 1812·2021-09-22 15:08
閱讀 2352·2021-09-07 10:24
閱讀 3582·2019-08-30 12:52