廣播與派發原理(基於vue實現dispatch, broadcast)

    廣播與派發適用於有祖先關係的組件間通信,原理是通過遍歷組件樹,用組件名(或者其它組件唯一標識)定位到對應的組件,並在找到的組件實例中調用$emit,從而觸發$on監聽的事件,這樣就完成了一次組件間的通信。廣播是父組件遍歷他下面的所有後代組件,而派發的順序則相反,是子組件向上尋找對應的父組件。
    在vue中我們可以通過this.$parent獲取父組件,this.$children獲取子組件,this.$options.name獲取當前實例的組件名,有了這些基礎知識,我們就可以來設計dispatch(派發)和broadcast(廣播)方法了。
    這裏我們把dispatch(派發)和broadcast(廣播)設計成mixins混入對象,畢竟用到他們的時候還是少數,往往開發組件庫的時候我們會用到。下面是emitter.js的代碼:
 

function broadcast (componentName, eventName, params) {
  for (let child of this.$children) {
    let name = child.$options.name
    if (name === componentName) {
      child.$emit.apply(child, [eventName, params])
    } else {
      broadcast.call(child, componentName, eventName, params)
    }
  }
}
export default {
  methods: {
    dispatch (componentName, eventName, params) {
      let parent = this.$parent || this.$root
      let name = parent.$options.name
      while (parent && (name !== componentName)) {
        parent = parent.$parent
        if (parent) name = parent.$options.name
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName, params])
      }
    },
    broadcast (componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params)
    }
  }
}

    這部分代碼最核心的地方就是向下遍歷子組件和向上尋找父組件。
    爲了測試一下dispatch 和 broadcast的效果,下面我們定義三個簡單的組件componentA, componentB, componentC。
    componentA的代碼如下:
 

<template>
  <div>
    componentA
    <slot />
  </div>
</template>
<script>
import Emitter from '@/mixins/emitter.js'
export default {
  name: 'componentA',
  mixins: [Emitter],
  created () {
    this.$on('dispatchB', this.dispatchB)
    this.$on('dispatchC', this.dispatchC)
  },
  mounted () {
    this.broadcast('componentC', 'broadcastA', 'componentA')
  },
  methods: {
    dispatchB (aim) {
      console.log('componentA:', aim, 'is dispatching')
    },
    dispatchC (aim) {
      console.log('componentA:', aim, 'is dispatching')
    }
  }
}
</script>

componentB的代碼如下: 

<template>
  <div>
    componentB
    <slot />
  </div>
</template>
<script>
import Emitter from '@/mixins/emitter.js'
export default {
  name: 'componentB',
  mixins: [Emitter],
  created () {
    this.dispatch('componentA', 'dispatchB', 'componentB')
  }
}
</script>

 componentC的代碼如下:

 

<template>
  <div>
    componentC
    <slot />
  </div>
</template>
<script>
import Emitter from '@/mixins/emitter.js'
export default {
  name: 'componentC',
  mixins: [Emitter],
  created () {
    this.dispatch('componentA', 'dispatchC', 'componentC')
  },
  mounted () {
    this.$on('broadcastA', this.broadcastA)
  },
  methods: {
    broadcastA (aim) {
      console.log('componentC:', aim, 'is broadcasting')
    }
  }
}
</script>

App.vue 代碼如下:
 

<template>
  <div id="app">
    <componentA>
      <componentB>
        <componentC />
      </componentB>
    </componentA>
  </div>
</template>

這裏我們的componentA向componentC廣播了一個事件,componentB和componentC向componentA分別派發了一個事件。下面是最後的信息打印結果。
 

 結果完全符合預期,dispatch和broadcast工作正常。值得注意的是vue組件的渲染順序是有內而外的,也就是mounted鉤子的調用順序是componentC, componentB, componentA。組件的創建順序是由外而內的,也就是created鉤子的調用順序是componentA,componentB,componentC。不管你是廣播事件也好,派發事件也罷,請務必注意你的$on監聽事件的順序一定是發生在他們之前的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章