/**
* Observer 類會附加到每一個被偵測的Object上
* 一旦被附加上,Observer會將Object的所有屬性都轉換
* 爲getter/setter的形式來手機屬性的以來
* 並且當屬性變化時會通知這些依賴
*/
export class Observer {
value: any;
dep: Dep;
vmCount: number; // 具有該對象作爲根$data的vm的數量
constructor (value: any) {
this.value = value
this.dep = new Dep() // 用來存放array的依賴
this.vmCount = 0
/**
* 給每一個值的原型上新增一個‘__ob__‘屬性
* ‘__ob__‘屬性的值就是當前Observer的實例
* 通過這個屬性就可以拿到數組的依賴了
*/
def(value, '__ob__', this)
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
}
/**
* walk方法會將每一個屬性都轉換成getter/setter
* 的形式來偵測變化,這個方法只在數據類型時
* Object的時候被調用
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* 偵測一個數組中的每一項
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
概述:
對於Observer類,這個類,就是給每一個對象,添加一個 __ob__
屬性,並且把這個對象,變成getter/setter形式。變成這樣的形式呢,會方便收集依賴,並且在數據發生變化的時候,通知各個依賴。
相關方法
-
Dep
類裏面使用this.dep = new Dep()
,是爲了存放數組的依賴,因爲數組在getter的時候收集依賴,在攔截器裏觸發依賴,將數組的依賴放在Observer的實例上,是爲了讓數組的getter和攔截器中都能訪問到。 -
def()
這個方法,是用來處理value,給它添加一個__ob__
的屬性,這個屬性的值就是當前Observer的實例,因爲數組的依賴保存在Observer的實例上,所以添加這個值,就可以在攔截器中訪問Observer實例,並拿到相應的依賴。
/**
* 定義一個屬性.
*/
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
// Observer 類中
def(value, '__ob__', this)
- 判斷value是對象還是數組,如果是對象的話,就走walk方法,walk方法就是把object自身有的屬性遍歷一邊,然後用
defineReactive()
方法全部轉換成getter/setter形式 - 如果是數組對象,就需要先判斷一下,瀏覽器能不能支持
__proto__
這個屬性,爲什麼要判斷這個屬性?因爲數組的某些原型中的方法,需要覆蓋,通過判斷hasProto
來用兩種方法來處理覆蓋value原型的功能,支持__proto__
,使用protoAugment()
函數覆蓋原型,如果不支持,則調用copyAugment()
函數將攔截器中的方法掛載到vulue上。
/**
* 使用__proto__截取原型鏈來增強目標對象或數組
*/
function protoAugment (target, src: Object) {
/* eslint-disable no-proto */
target.__proto__ = src
/* eslint-enable no-proto */
}
/**
* 通過定義一個隱藏屬性來增強目標對象或數組
*/
/* istanbul ignore next */
function copyAugment (target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key])
}
}
observeArray()
方法
循環Array中的每一項,執行observe函數來偵測變化,通過observe函數,將數組中的每一個元素都執行一遍new Observer()
。observe()
/**
* 嘗試爲value創建一個Observer的實例。
* 如果創建成功,直接返回新創建的Observer實例
* 如果value已經存在一個Observer實例,就直接返回它
*/
export function observe (value: any, asRootData: ?boolean): Observer | void {
// 不是對象或者不是VNode的實例,就返回
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 有observer實例就返回該實例
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
// 否則返回新增的實例
return ob
}