iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 18
0
Modern Web

技術在走,Vue.js 要有系列 第 18

|D18| 從原始碼看 Vue 響應式原理 (4) - Dep

  • 分享至 

  • xImage
  •  

Vue 透過 Observer 將普通物件加上 getterssetter,使其變成響應式物件,
響應式物件與 getters 相關的邏輯就是做 依賴收集

那為什麼要進行依賴收集?
下面範例中,畫面並沒有用到 title3,但若我們執行 this.title3 = 'D' 修改 title3 數據時,會觸發title3setter ,導致畫面重新渲染,這樣母湯

new Vue({
    template: 
        `<div>
            <div>標題1</div>
            <div>{{ title1 }}</div>
            <div>標題2</div>
            <div>{{ title2 }}</div>
        </div>`,
    data: {
        title1: 'A',
        title2: 'B',
        title3: 'C'
    }
});

當發生了改變 data 的行為,例如this.title3 = 'D',observe 的 set 方法會發現 data 中屬性改變,進而通知 dep 有 data 改變了,然後 dep 通知相關的 watcher,使其執行 watcher 的 updater function,完成畫面更新。

Dep 是 getter 依賴收集的核心,回顧一下 defineReactive 的 getter 邏輯

// src/core/observer/index.js

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

 //...

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    /*************************************************
     get function 透過 dep.depend 做依賴收集
   **************************************************/ 
    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
    },
    // ...
  })
}

Dep class 定義了一個靜態屬性 targetDep.target 就是一個 Watcher
subs 陣列是用來儲存 Watcher,當 data 屬性值發生改變,在 subs 陣列裡的所有 Watcher 都會收到更新通知

// src/core/observer/dep.js

export default 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 () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

Dep.target = null
const targetStack = []

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

export function popTarget () {
  Dep.target = targetStack.pop()
}

上一篇
|D17| 從原始碼看 Vue 響應式原理 (3) - Observer
下一篇
|D19| 從原始碼看 Vue 響應式原理 (5) - Watcher
系列文
技術在走,Vue.js 要有30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言