Vue的依賴收集簡單圖解

vue中利用Object.defineProperty定義getter/setter實現了數據的響應式,裏面重要的一點就是依賴收集,它是計算屬性和被依賴屬性之間的橋樑,有了依賴收集(Dep),當被依賴對象A的getter函數執行時,全部依賴它的東東就會被收集到A的依賴庫中,當A的setter執行時,依賴庫中的東東就會被一個一個執行,通知依賴對象B。而這些被封裝的依賴是在B的getter執行的時候注入到Dep的靜態屬性target中的javascript

圖很醜,包涵,下面的數字標號如①表示代碼執行的順序vue

 

/*
* 定義一個「依賴收集器」
* */
class Dep {
  constructor () {
    this.deps = []
  }

  /* 收集依賴 */
  depend () {
    if (Dep.target && this.deps.indexOf(Dep.target) === -1) {
      this.deps.push(Dep.target)
    }
  }

  /* 執行依賴 */
  notify () {
    this.deps.forEach((dep) => {
      dep()
    })
  }
}

/* target是被觀察者的回調執行結果和計算屬性被更新後調用的函數的封裝 */
Dep.target = null

/* 把一個對象的每一項都轉化成可觀測對象 */
class Observable {
  constructor (obj) {
    return this.walk(obj)
  }

  walk (obj) {
    const keys = Object.keys(obj)
    keys.forEach((key) => {
      this.defineReactive(obj, key, obj[key])
    })
    return obj
  }

  /* 使一個對象轉化成可觀測對象 */
  defineReactive (obj, key, val) {
    const dep = new Dep()
    Object.defineProperty(obj, key, {
      get () {
        dep.depend()
        return val
      },
      set (newVal) {
        val = newVal
        dep.notify()
      }
    })
  }
}

/*
* 觀察者
* obj: 被觀察對象
* key: 被觀察者key
* cb: 回調函數,返回「計算屬性」的值
* onComputedUpdate: 當計算屬性的值被更新時調用*/
class Watcher {
  constructor (obj, key, cb, onComputedUpdate) {
    this.obj = obj
    this.key = key
    this.cb = cb
    this.onComputedUpdate = onComputedUpdate
    return this.defineComputed()
  }

  defineComputed () {
    const self = this
    const onDepUpdated = () => {
      const val = self.cb()
      this.onComputedUpdate(val)
    }

    Object.defineProperty(self.obj, self.key, {
      get () {
        Dep.target = onDepUpdated
        const val = self.cb()
        Dep.target = null
        return val
      },
      set () {
        console.error('計算屬性沒法被賦值!')
      }
    })
  }
}

 

運行一下:java

const hero = new Observable({
  health: 3000,
  IQ: 150
})

new Watcher(hero, 'type', () => {
  return hero.health > 4000 ? '坦克' : '脆皮'
}, (val) => {
  console.log(`個人類型是:${val}`)
})

console.log(`英雄初始類型:${hero.type}`)

hero.health = 5000

// -> 英雄初始類型:脆皮
// -> 個人類型是:坦克

 

 

參考資料:函數

https://zhuanlan.zhihu.com/p/29318017this

相關文章
相關標籤/搜索