EventBus的简介
EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次
如何使用EventBus
在Vue的项目中怎么使用 EventBus 来实现组件之间的数据通讯呢?具体可以通过下面几个步骤来完成
初始化
首先你需要做的是创建事件总线并将其导出,以便其它模块可以使用或者监听它。我们可以通过两种方式来处理。先来看第一种,新创建一个 .js 文件,比如 event-bus.js :
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
你需要做的只是引入 Vue 并导出它的一个实例(在这种情况下,我称它为 EventBus )。实质上它是一个不具备 DOM 的组件,它具有的仅仅只是它实例方法而已,因此它非常的轻便
另外一种方式,可以直接在项目中的 main.js 初始化 EventBus :
// main.js
Vue.prototype.$EventBus = new Vue()
注意,这种方式初始化的 EventBus 是一个 全局的事件总线 。稍后我们会花点时间专门聊一聊全局的事件总线。
现在我们已经创建了 EventBus ,接下来你需要做到的就是在你的组件中加载它,并且调用同一个方法,就如你在父子组件中互相传递消息一样
发送事件
假设你有两个子组件: DecreaseCount 和 IncrementCount ,分别在按钮中绑定了 decrease()和 increment() 方法。这两个方法做的事情很简单,就是数值递减(增) 1 ,以及角度值递减(增) 180 。在这两个方法中,通过 EventBus.$emit(channel: string, callback(payload1,…)) 监听 decreased 和 incremented 频道。
<!-- DecreaseCount.vue -->
<template>
<button @click="decrease()">-</button>
</template>
<script>
import { EventBus } from "../event-bus.js";
export default {
name: "DecreaseCount",
data() {
return {
num: 1,
deg:180
};
},
methods: {
decrease() {
EventBus.$emit("decreased", {
num:this.num,
deg:this.deg
});
}
}
};
</script>
<!-- IncrementCount.vue -->
<template>
<button @click="increment()">+</button>
</template>
<script>
import { EventBus } from "../event-bus.js";
export default {
name: "IncrementCount",
data() {
return {
num: 1,
deg:180
};
},
methods: {
increment() {
EventBus.$emit("incremented", {
num:this.num,
deg:this.deg
});
}
}
};
</script>
上面的示例,在 DecreaseCount 和 IncrementCount 分别发送出了 decreased 和 incremented频道。接下来,我们需要在另一个组件中接收这两个事件,保持数据在各组件之间的通讯
接收事件
现在我们可以在组件 App.vue 中使用 EventBus.$on(channel: string, callback(payload1,…))监听 DecreaseCount 和 IncrementCount 分别发送出了 decreased 和 incremented 频道。
<!-- App.vue -->
<template>
<div id="app">
<div class="container" :style="{transform: 'rotateY(' + degValue + 'deg)'}">
<div class="front">
<div class="increment">
<IncrementCount />
</div>
<div class="show-front"> {{fontCount}} </div>
<div class="decrement">
<DecreaseCount />
</div>
</div>
<div class="back">
<div class="increment">
<IncrementCount />
</div>
<div class="show-back"> {{backCount}} </div>
<div class="decrement">
<DecreaseCount />
</div>
</div>
</div>
</div>
</template>
<script>
import IncrementCount from "./components/IncrementCount";
import DecreaseCount from "./components/DecreaseCount";
import { EventBus } from "./event-bus.js";
export default {
name: "App",
components: {
IncrementCount,
DecreaseCount
},
data() {
return {
degValue:0,
fontCount:0,
backCount:0
};
},
mounted() {
EventBus.$on("incremented", ({num,deg}) => {
this.fontCount += num
this.$nextTick(()=>{
this.backCount += num
this.degValue += deg;
})
});
EventBus.$on("decreased", ({num,deg}) => {
this.fontCount -= num
this.$nextTick(()=>{
this.backCount -= num
this.degValue -= deg;
})
});
}
};
</script>
如果你只想监听一次事件的发生,可以使用 EventBus.$once(channel: string, callback(payload1,…))
移除事件监听者
如果想移除事件的监听,可以像下面这样操作:
import { eventBus } from './event-bus.js'
EventBus.$off('decreased', {})
你也可以使用 EventBus.$off(‘decreased’) 来移除应用内所有对此事件的监听。或者直接调用EventBus.$off() 来移除所有事件频道, 注意不需要添加任何参数
事件总线使用时注意事项:
两个问题:
- 问题1: 为什么第一次触发的时候页面B中的on事件没有被触发
- 问题2: 为什么后面再一次依次去触发的时候会出现,每一次都会发现好像之前的on事件分发都没有被撤销一样,导致每一次的事件触发执行越来越多。
问题一
第一次触发的时候页面B中的on事件没有被触发
产生原因
当我们还在页面A的时候,页面B还没生成,也就是页面B中的
created中所监听的来自于 A 中的事件还没有被触发。这个时候当你 A 中emit事件的时候,B其实是没有监听到的。解决办法:
我们可以把 A 页面组件中的
emit事件写在beforeDestory中去。因为这个时候,B 页面组件已经被created了,也就是我们写的on事件已经触发了,所以可以在beforeDestory的时候,on事件已经触发了,所以可以在beforeDestory的时候,on事件已经触发了,所以可以在beforeDestory的时候,emit事件问题二
后面再一次依次去触发的时候会出现,每一次都会发现好像之前的on事件分发都没有被撤销一样,导致每一次的事件触发执行越来越多。
产生原因
就是说,这个
$on事件是不会自动清楚销毁的,需要我们手动来销毁。(不过我不太清楚这里的external bus是什么意思,有大神能解答一下的吗,尤大大也提到如果是注册的是external bus的时候需要清除)解决办法:
在B组件页面中添加Bus.$off来关闭
// 在B组件页面中添加以下语句,在组件beforeDestory的时候销毁。 beforeDestroy () { bus.$off('get', this.myhandle) }总结
所以,如果想要用 bus 来进行页面组件之间的数据传递,需要注意两点:
一、组件A emit 事件应在beforeDestory生命周期内。
二、组件B内的 on 记得要销毁