廣播與派發適用於有祖先關係的組件間通信,原理是通過遍歷組件樹,用組件名(或者其它組件唯一標識)定位到對應的組件,並在找到的組件實例中調用$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監聽事件的順序一定是發生在他們之前的。