Vue事件總線(EventBus)、$on、$emit、$off


在之前我們只用過父傳子,子傳父進行傳數據,這時候當組件嵌套比較深或比較復雜的情況,這時候就用到了事件總線 (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('事件名', 回調) 會取消指定事件名的,指定回調


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM