Observer源碼解析

前言

在Observer源碼部分主要有三個角色:Observer、Dep、Watcher。node

Observer

看這一部分,能夠帶着如下幾個問題來看:react

一、知足什麼條件能夠將其變成響應式的數組

二、Observer是如何去分別處理傳入的數組或者對象的?ui

三、有兩處new Dep,做用分別是什麼?this

四、核心代碼defineReactive幹了些什麼?spa

五、Dep.target是什麼?defineReactive中get時,爲何要判斷Dep.target?code

知足什麼條件能夠將其變成響應式的

function observe (value, asRootData) {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob
  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
}
複製代碼

其實上述代碼中有不少判斷,咱們能夠得出如下結論:cdn

(1)必須是一個對象,且不能是vnode的類型的。server

Observer是如何去分別處理傳入的數組或者對象的

if (Array.isArray(value)) {
  if (hasProto) {
    protoAugment(value, arrayMethods)
  } else {
    copyAugment(value, arrayMethods, arrayKeys)
  }
  this.observeArray(value)
} else {
  this.walk(value)
}
複製代碼

這段代碼就是判斷傳入的值是否是數組,若是是數組,走observeArray方法,若是不是數組,那麼走walk方法。對象

walk (obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
}
observeArray (items) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
}
複製代碼

walk這個方法就是遍歷對象,而後給對象中的屬性值變成響應式的,遍歷完以後,整個對象就是響應式對象了。 observeArray這個方法是遍歷數組,而後對數組中每個元素在走一遍響應式流程。

有兩處new Dep,做用分別是什麼

第一處:

class Observer {
  constructor (value) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    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)
    }
  }
複製代碼

第二處:

function defineReactive ( obj, key, val, customSetter, shallow ) {
  const dep = new Dep()
  // 省略中間代碼
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        // 省略中間代碼
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // 省略中間代碼
      dep.notify()
    }
  })
}
複製代碼

第二處dep咱們很好理解,是給get和set服務的,進行依賴收集和派發更新。

第一處dep,咱們能夠考慮一下,他是整個對象的一個屬性,那麼他什麼時候進行依賴收集和派發更新。

咱們能夠全局搜一下dep.depend。發現會有三處。有兩處是對ob屬性進行操做的,也就是對整個對象進行依賴收集。

在全局搜一下dep.notify。發現有四處。有三處是對ob屬性進行操做的。分別是set和del,數組的一個方法。

核心代碼defineReactive幹了些什麼

這一部分代碼能夠分爲三部分看:定義一些變量、Object.defineProperty、對childOb操做。

// 生成一個新的dep。
  const dep = new Dep()
  // 判斷這個對象這個屬性是否能夠修改
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
  // 定義getter和setter方法
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
複製代碼
get: function reactiveGetter () {
  // 獲取值
  const value = getter ? getter.call(obj) : val
  // 當存在Dep.target的時候進行依賴收集
  if (Dep.target) {
    dep.depend()
    // 省略中間代碼
  }
  // 返回獲取到的值
  return value
},
set: function reactiveSetter (newVal) {
  // 獲取原來的值
  const value = getter ? getter.call(obj) : val
  // 將新的值和原值進行對比,若是沒有發生改變,就直接返回
  if (newVal === value || (newVal !== newVal && value !== value)) {
    return
  }
  // 該屬性不能set的狀況也直接返回
  if (getter && !setter) return
  // 給該屬性賦值
  if (setter) {
    setter.call(obj, newVal)
  } else {
    val = newVal
  }
  // 從新對這個值進行監聽
  childOb = !shallow && observe(newVal)
  // 更新dep中的watcher
  dep.notify()
}
複製代碼
// 嘗試將值轉化成響應式對象
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) {
      // 省略中間代碼
      //由於值發生了變更,因此再一次嘗試將其變成一個響應式對象
      childOb = !shallow && observe(newVal)
    }
  })
複製代碼

Dep.target是什麼

其實Dep.target是一個全局變量,更是一個wathcer。在dep文件中的源碼以下:

Dep.target = null
const targetStack = []

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}
複製代碼

defineReactive中get時,爲何要判斷Dep.target

由於全部定義在data中的值,都會被變成響應式對象,可是每個不必定有watcher。watcher分爲三種:render中生成的watcher、用戶自定義的watcher、computed。

上述流程中,當生成一個新的renderWatcher的時候,便會走get流程,而後進行依賴收集,若是沒有Dep.target,說明這個值並無對應的watcher,因此不須要進行依賴收集。 當更新的是時候,又回進行一次依賴收集。

相關文章
相關標籤/搜索