看這篇之前,假如沒有看過之前的文章,可拉到文章末端檢察之前的文章。
回憶
在前面的幾個 step
中,我們完成對象的屬性的監聽,然則有關於數組的行動我們一向沒有處置懲罰。
我們先剖析下致使數組有哪些行動:
- 挪用要領:
arr.splice(1, 2, 'something1', 'someting2')
- 直接賦值:
arr[1] = 'something'
處理行動一
起首我們曉得數組下的一些要領是會對原數組照成影響的,有以下幾個:
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
這幾個要領總的來說會照成幾個影響:
- 數組長度發生變化
- 數組內元素遞次發生變化
不像對象,假如對象的 key
值的遞次發生變化,是不會影響視圖的變化,但數組的遞次假如發生變化,視圖是要變化的。
也就是說當著幾個要領觸發的時刻,我們須要視圖的更新,也就是要觸發 Dep
中的 notify
函數。
然則縱觀我們如今完成的代碼( step5
中的代碼),我們並沒有專程的為數組供應一個 Dep
。
而且上述的幾個數組要領是數組對象供應的,我們要想方法去觸發 Dep
下的 notify
函數。
我們先為數組供應一個 Dep
,完善後的 Observer
:
export class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
// 為數組設置一個特別的 Dep
this.dep = new Dep()
this.observeArray(value)
} else {
this.walk(value)
}
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]])
}
}
/**
* 同上,遍曆數組
*/
observeArray (items) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
一樣的在 defineReactive
我們須要處置懲罰數組增加依靠的邏輯
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)
// 處置懲罰數組的依靠
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 我們如今完成了依靠的增加,剩下的我們要完成依靠的觸發。
處置懲罰要領:在數組對象挪用特定要領時,起首找到的應該是我們本身寫的要領,而這個要領中挪用了原始要領,並觸發依靠。
我們先來包裝一下要領,獲得一些同名要領:
const arrayProto = Array.prototype
// 複製要領
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* 轉變數組的默許處置懲罰,將新增加的對象增加監聽
*/
methodsToPatch.forEach(function (method) {
// 原始的數組處置懲罰要領
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
}
// 新增加的對象須要增加監聽
if (inserted) ob.observeArray(inserted)
// 觸發 notify 要領
ob.dep.notify()
return result
}
Object.defineProperty(arrayMethods, method, {
value: mutator,
enumerable: false,
writable: true,
configurable: true
})
})
ok 我們如今獲得了一些列同名的要領,我只需確保在挪用時,先挪用到我們的要領即可。
有兩種體式格局能夠完成:
- 數組對象上直接有該要領,如許就不會去找對象上的原型鏈
- 掩蓋對象的
__proto__
,如許尋覓原型鏈時,就會先找到我們的要領
詳細到代碼中的完成:
export class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
this.dep = new Dep()
const augment = ('__proto__' in {})
? protoAugment
: copyAugment
// 掩蓋數組中一些轉變了原數組的要領,使得要領得以監聽
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
...
}
...
}
/**
* 假如能運用 __proto__ 則將數組的處置懲罰要領舉行替代
*/
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(`監聽函數,數組內一切元素 = ${newValue}`)
})
object.arrayTest.push(10)
// 監聽函數,數組內一切元素 = 25
到如今為止,我們勝利的在數組挪用要領的時刻,增加並觸發了依靠。
處理行動二
起首先申明,數組下的索引是和對象下的鍵有一樣的表現,也就是能夠用 defineReactive
來處置懲罰索引值,然則數組是用來寄存一系列的值,我們並不能一開始就肯定數組的長度,而且極有能夠剛開始數組長度為 0
,以後數組中的索引對應的內容也會不停的變化,所認為索引挪用 defineReactive
是不切實際的。
然則類似於 arr[1] = 'something'
如許的賦值在數組中也是罕見的操縱,在 Vue
中完成了 $set
詳細的細節這裏不談,這裏完成了另一種要領,我們僅僅須要在數組對象下增加一個要領即可:
arrayMethods.$apply = function () {
this.__ob__.observeArray(this)
this.__ob__.dep.notify()
}
測試一下:
object.arrayTest[1] = 10
object.arrayTest.$apply()
// 監聽函數,數組內一切元素 = 33
到現在為了,一個完全的數據監聽的模子也就完成了,我們能夠運用 observe
要領來獲得一個可監聽構造,然後用 Watcher
增加依靠。
在設置值的時刻就可以勝利觸發依靠。
系列文章地點
- VUE – MVVM – part1 – defineProperty
- VUE – MVVM – part2 – Dep
- VUE – MVVM – part3 – Watcher
- VUE – MVVM – part4 – 優化Watcher
- VUE – MVVM – part5 – Observe
- VUE – MVVM – part6 – Array
- VUE – MVVM – part7 – Event
- VUE – MVVM – part8 – 優化Event
- VUE – MVVM – part9 – Vue
- VUE – MVVM – part10 – Computed
- VUE – MVVM – part11 – Extend
- VUE – MVVM – part12 – props
- VUE – MVVM – part13 – inject & 總結