【前端進階基礎】VUE響應式數據原理 訂閱-發佈模式解析

vue框架的兩個抽象核心:虛擬DOM和相應式數據原理vue

關於虛擬DOM的核心算法,咱們上一章已經基本解析過了,詳細的見
React && VUE Virtual Dom的Diff算法統一之路 snabbdom.js解讀react

關於響應式數據原理,咱們先看張圖
你 ‘ (4).png
clipboard.png算法

具體來說,要分如下幾步:segmentfault

  • 初始化實例對象時運行initState, 創建好props, data 的鉤子以及其對象成員的Observer, 對於computed 屬性,則創建起全部對應的 Watcher 而且經過 Object.defineProperty 在vm對象上設置一個該屬性的 getter。同時還根據自定義的 watch 來創建相應的 Watcher
  • 執行掛載操做,在掛載時創建一個直接對應render(渲染)的 Watcher,而且編譯模板生成 render 函數,執行vm._update 來更新 DOM 。
  • 此後每當有數據改變,都將通知相應的 Watcher 執行回調函數。

Observer 劫持者

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through each property and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

首先咱們觀察到,new Observer()的時候,會進行類型的判斷數組

if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }

若是是數組類型則會遞歸調用,創建依賴體系
不然則調用walk()函數去利用Object.defineProperty簡歷依賴框架

那麼defineReactive是如何實現的呢,以下dom

//省略了部分代碼
export function defineReactive (
 obj: Object,
 key: string,
 val: any,
 customSetter?: ?Function,
 shallow?: boolean
) {
  const dep = new Dep()
  ...
  Object.defineProperty(obj, key, {
      ...
      get: function reactiveGetter () {
        ...
        dep.depend()
        ...
        return value
      },
      set: function reactiveSetter (newVal) {
        ...
        dep.notify()
      }
    })
}

攔截器會分別在getter和setter上設置中間件去維護dep數組中的依賴關係函數

  • dep.depend()
  • dep.notify()

字面意思很是明顯,分別用於增長依賴和通知依賴變化關係ui

Dep (dependency // 依賴)

class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

Dep 就是一個 Watcher 所對應的數據依賴,在這個對象中也存有一個 subs 數組,用來保存和這個依賴有關的 Watcher。其成員函數最主要的是 depend 和 notify ,前者用來設置某個 Watcher 的依賴,後者則用來通知與這個依賴相關的 Watcher 來運行其回調函數。this

咱們知道,Dom 上經過指令或者雙大括號綁定的數據,會爲數據進行添加觀察者watcher,當實例化Watcher的時候 會觸發屬性的getter方法,此時會調用dep.depend()。咱們看一下depend方法:

depend () {
  if (Dep.target) {
    Dep.target.addDep(this)
  }
}

Dep.target 是什麼東西呢?其實在進行Watcher實例化的時候,會調用內部的get函數,開始爲他初始化

Watcher 觀察者

其中pushTarget 方法就是爲Dep.target綁定此watcher實例,因此Dep.target.addDep(this)也就是執行此實例中的addDep方法

addDep (dep: Dep) {
  ...
  dep.addSub(this)
}

這樣便爲咱們的dep實例添加了一個watcher實例。

接着當咱們更新data的時候,會觸發他的set方法,執行dep.notify()函數:

notify () {
  // stabilize the subscriber list first
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
    subs[i].update()
  }
}

這裏也就是遍歷dep中收集到的watcher實例,進行update()。也就是進行數據更新操做。這也就是簡單的數據響應式,其實還須要注意的是: 當數據的getter觸發後,會收集依賴,但也不是全部的觸發方式都會收集依賴,只有經過watcher觸發的getter會收集依賴:if (Dep.target) { dep.depend() },而所謂的被收集的依賴就是當前watcher,DOM中的數據必須經過watcher來綁定,只經過watcher來讀取。

最後付一個函數timeline
的.png
clipboard.png

相關文章
相關標籤/搜索