在之前我們只用過父傳子,子傳父進行傳數據,這時候當組件嵌套比較深或比較復雜的情況,這時候就用到了事件總線 (EventBus)
如何理解事件總線呢,你可以理解為用來傳輸數據的一條線
注意點:有組件發布事件后 剩余的所有組件都可以進行監聽事件
一、使用EventBus
1. 創建事件總線 main.js
import Vue from 'vue'
// 創建事件總線 就相當於創建了一個新的vue實例
const bus = new Vue()
// 把bus掛載到了Vue的原型上, 保證所有的組件都能通過 this.$bus訪問到事件總線
Vue.prototype.$bus = bus
2. 頁面使用 發布事件 - 傳遞值
// this.$bus.$emit('事件名', 額外參數)
this.$bus.$emit('send', 'hello')
3.訂閱事件 - 接收組件值
// 1. 在created中訂閱
// 2. 回調函數需要寫成箭頭函數
// this.$bus.$on('事件名', 事件回調函數)
this.$bus.$on('send', msg => {
console.log(msg)
})
二、注意點
$on先執行 在執行$emit觸發事件 (嵌套關系)
知道此問題必須先要知道父子級組件嵌套關系的鈎子函數執行順序?如果你對生命周期鈎子函數不了解先來看 生命周期的詳解
父子嵌套生命周期 會先執行父組件中的前三個后 執行子組件的前三個 再去執行父組件的mounted
接下倆我們做個實驗 看看為什么先執行$on 在執行$emit
父組件中
<template> <div> <son></son> </div> </template> <script> import son from './Son.vue' export default { components: { son }, created() { console.log('父組件:我被創建啦') this.$bus.$emit('bus', 10) } } </script>
子組件
<template> <div> {{message}} </div> </template> <script> export default { data () { return { message: '' } }, created () { console.log('子組件:我被創建啦') this.$bus.$on('bus', (val) => { console.log(val) this.message = val }) }, } </script>
這時候是看不見任何接收信息的
原因就是 沒有等子組件$on去注冊事件后就執行了父組件$emit 所以接收不到信息
解決方法:把父組件中的$emit事件放在mounted鈎子函數中 等待子組件創建並注冊$on事件后再去觸發$emit。 只適合嵌套關系 可能出現兄弟關系 也要看情況進行使用
mounted () {
console.log('父組件:mounted')
this.$bus.$emit('bus', 10)
}
三、移除事件監聽
來看個栗子
父組件中沒兩秒進行一次傳遞 點擊v-if后將子組件進行 銷毀或重新創建
<template> <button @click="isShow=!isShow"> {{isShow?'銷毀':'重建'}} </button> <son v-if="isShow=isShow">son</son> </div> </template> <script> import son from './Son.vue' export default { components: { son }, data () { return { isShow: true } }, created () { console.log('父組件:我被創建啦') setInterval(() => { this.$bus.$emit('bus', 10) }, 2000) } }
子組件
<template> <div> {{message}} </div> </template> <script> export default { data () { return { message: null } }, created () { console.log('子組件:我被創建啦') this.$bus.$on('bus', (val) => { console.log(val) this.message = val }) } } </script>
問題點:當點擊銷毀子組件的時候 子組件還能接收到數據嗎?對應的回調函數還能在執行嗎? 答案是 會,事件訂閱功能是$eventBus對象完成的,與組件無關,當你點擊銷毀后 再點擊創建又會多了一個訂閱事件,依次類推每次點擊新建后都會多一個訂閱事件
造成原因:事件訂閱是通過$eventBus對象完成的 與組件無關
如果不移除事件監聽 並且會造成內存泄漏
在son組件中修改
created () {
console.log('子組件:我被創建啦')
const m = 1 * 1024 * 1024
const arr = new Array(m).fill('a')
this.$bus.$on('bus', function f1 (val) {
// 注意這里有一個閉包
console.log(val, 'son listen... bus', arr[1])
})
},
通過v-if 銷毀和重新創建來看通過數據發現 銷毀后並沒有對空間進行釋放
解決方案: 在子組件銷毀后進行取消訂閱事件
destroyed () {
// 取消對bus事件的監聽
this.$bus.$off('bus')
}
對bus取消事件監聽后 內存得到了釋放
總結:
1. 任何組件都可以在事件總線中發布事件 this.$bus.$emit('xxx','傳遞參數')
2. 任何組件都可以在事件總線中監聽事件 this.$bus.$on('xxx',(接收參數)=>{ 對形參進行操作 })
3. main.js注冊空的Vue對象, 只負責$on注冊事件, $emit觸發事件, 一定要確保$on先執行
4.$off的格式:
$off() 會取消所有的事件訂閱;
$off('事件名') 會取消指定事件名的;
$off('事件名', 回調) 會取消指定事件名的,指定回調