1. 程式人生 > >淺析Vue響應式原理(三)

淺析Vue響應式原理(三)

Vue響應式原理之defineReactive

defineReactive

不論如何,最終響應式資料都要通過defineReactive來實現,實際要藉助ES5新增的Object.defineProperty

defineReactive接受五個引數。obj是要新增響應式資料的物件;key是屬性名,val是屬性名對應的取值;customSetter是使用者自定義的setter;會在響應式資料的setter中執行,只有開發環境可用;通過shallow指定是否淺比較,預設深比較。


export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  const getter = property && property.get
  if (!getter && arguments.length === 2) {
    val = obj[key]
  }
  const setter = property && property.set

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}

在函式內,首先例項化一個Dep例項depdep會在稍後新增為響應式資料自定義的get/set中發揮作用。接著獲取屬性描述符,如果屬性不可配置,則無法呼叫Object.defineProperty來修改setter/getter,所以返回。

如果原來已設定過setter/getter,快取起來。當未自定義getter且arguments長度為2(即只傳入了objkey)時,可以直接用方括號求值,使用閉包變數val快取初始值。

如果不是淺複製,執行observe(val),為val新增__ob__屬性並返回__ob__指向的Observer例項。(只有陣列和物件才可能是響應式,才能返回Observer例項)。

使用Object.definePropertyobj[key]設定getter和setter。

get內,如果原來已設定過getter,則用快取的getter求值,否則使用閉包變數val作為返回值;同時新增依賴。此處為兩個Dep例項新增依賴。dep是閉包變數,在getter/setter中會使用到。另一個Dep例項是childOb.dep,只用呼叫set/delete更新響應式資料時,才會觸發;如果value是陣列,還會遍歷元素,為存在__ob__屬性的元素收集依賴。

set內,先獲取更新前的值(邏輯和get內第一步一樣)。判斷更新前後的值是否相等,相等時直接返回;不等時,如果有快取的setter,呼叫快取的setter更新,否則直接賦值。值得注意的是,NaN === NaN

是不成立的,反而NaN !== NaN是成立的,後面的判斷語句newVal !== newVal && value !== value就是為了避免newVal/val都是NaN。在更新後的值newVal上執行observe,更新閉包變數childOb,並呼叫notify。

參考連結

原文地址:https://segmentfault.com/a/1190000017216175