【core/observer】之Observer類

/**
 * 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形式。變成這樣的形式呢,會方便收集依賴,並且在數據發生變化的時候,通知各個依賴。

相關方法

  1. Dep
    類裏面使用this.dep = new Dep(),是爲了存放數組的依賴,因爲數組在getter的時候收集依賴,在攔截器裏觸發依賴,將數組的依賴放在Observer的實例上,是爲了讓數組的getter和攔截器中都能訪問到。

  2. 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) 
  1. 判斷value是對象還是數組,如果是對象的話,就走walk方法,walk方法就是把object自身有的屬性遍歷一邊,然後用defineReactive()方法全部轉換成getter/setter形式
  2. 如果是數組對象,就需要先判斷一下,瀏覽器能不能支持__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])
  }
}
  1. observeArray()方法
    循環Array中的每一項,執行observe函數來偵測變化,通過observe函數,將數組中的每一個元素都執行一遍new Observer()
  2. 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
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章