全局事件总线
原理分析
全局事件总线能实现任意两个组件间的通信
例如我们现在有如下的结构:
我们想要实现任意两组件的通信,少不了一个中介X(图中右上角的粉色圆圈)。例如现在D组件要向A组件传一点数据,过程如下:
- 在A组件里面写点代码,给X绑定一个自定义事件(假设这个自定义事件的名称和叫做demo)
- 在D中写一段代码去触发X中的demo事件
- 在触发事件的同时带点数据过去(比如说我们带个666)
- 此时数据就以参数的形式来到了A组件中
在这个过程中,我们不难发现这个类似于中介的X,是有一定的要求的:
- 他能被其他所有组件给看见
- 这个X能调用
$on(),$off(),$emit()
等方法
首先我们可以讨论第一个要求怎么实现,我们有如下几种方法:
- 借用浏览器对象window,所有的组件对它一定是可视的(此方法可行,但不推荐)
- 既然对所有的组件对象可见,那么放在VueComponent构造函数的原型对象上(这个方法不可行,因为VueComponent构造函数是通过Vue.extend得来的,而每次得到的都是不同的VueComponent)
- 在上一个方法的基础上,修改源码,每出现一个新的VueComponent,就挂载一个X(此方法可行,但不推荐)
- 利用关系
VueComponent.prototype.__proto__ === Vue.prototype
,也就是说我们把X放在Vue.prototype身上,那么所有的组件都可以搜寻到他。(此法推荐)
接下来我们实现第二个要求:
$on(),$off(),$emit()
等方法都存在于Vue的原型对象上。
因为Vue原型对象上的属性和方法都是给Vue的实例对象(vm)或组件的实例对象(vc)用的,所以这个X我们必须创建为Vue的实例对象或者组件的实例对象。
不过我们普遍强调只能由一个Vue的实例对象,所以我们一般使用组件的实例对象来代替这个中介X
代码实现
例如我们现在有如下的组件结构:
我们想实现两个兄弟组件间的通信,Student组件将学生姓名交给School组件,步骤如下:
①首先创建好中介X
我们写在main.js中,因为Vue是在那个时候被引入的
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
const Demo = Vue.extend()
const d = new Demo()
Vue.prototype.talkHelper = d
new Vue({
render: h => h(App),
}).$mount('#app')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
②接受者方(School组件)去在中介上绑定事件
School组件:
<template>
<div class="demo">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<!-- <button @click="getSchoolName(name)">点击将学校名称交给父组件App</button>-->
<hr>
</div>
</template>
<script>
export default {
name:'DongBei',
data(){
return {
name:'NEFU',
address:'哈尔滨',
}
},
// props:['getSchoolName'],
methods:{
studentNameDeliver(name){
console.log('School组件已经接收到了来自Studennt组件的学生名称',name)
}
},
mounted() {
this.talkHelper.$on('studentName',this.studentNameDeliver)
}
}
</script>
<style >
.demo {
background-color: yellow;
}
</style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
③发送者方(Student组件)去调用中介身上的方法
Student组件:
<template>
<div class="demo">
<h2 class="stu" >学生名称:{{name}}</h2>
<h2 >学生年纪:{{age}}</h2>
<button @click="deliverStudentName">把学生名称给School组件</button>
</div>
</template>
<script>
export default {
name:'MyStudent',
data(){
return {
name:'张三',
age:18
}
},
methods:{
deliverStudentName(){
this.talkHelper.$emit('studentName',this.name)
}
}
}
</script>
<style >
/*.demo {*/
/* background-color: green;*/
/*}*/
</style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
最终的效果:
代码优化
在此处单独创建一个组件实例对象vc太麻烦,我们不如就使用Vue的实例对象vm来作为这个中介。
不过这么写是有错误的。时机有点晚了!因为当前三行vm创建完毕之后,App组件整个都已经放在页面上去了,这就意味着School组件上的mounted都已经执行完了!
此时你再去Vue的原型对象上放一个中介已经晚了!
所以说这个中介放入的时机非常的重要,它既要在vm定义之后,又要在App组件放入之前。所以这个时候我们就想到了要用生命周期钩子!
我们选择使用beforeCreate(),这个时候模板还没有开始解析
于是我们改进后的代码如下:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// const Demo = Vue.extend()
// const d = new Demo()
//
// Vue.prototype.talkHelper = d
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.talkHelper = this
}
}).$mount('#app')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这个中介其实就是全局事件总线,我们一般用$bus来进行标识
也就是说如上的代码我们应该写成:
还有一个注意点:在组件销毁之前,我们应该把其绑定在全局事件总线上的事件给解绑!
在自定义事件中我们说解绑这个操作是可有可无的,因为当组件销毁死亡之后,其身上的自定义事件自然就会消失。但是这里的全局事件总线他是一直存在的,即使有一个组件销毁了,但是绑定在全局事件总线上的事件还在,这就会造成资源的浪费。所以在这里我们推荐:在组件销毁之前,我们应该把其绑定在全局事件总线上的事件给解绑
我们使用beforeDestroy()生命周期钩子来执行解绑操作:
<template>
<div class="demo">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<hr>
</div>
</template>
<script>
export default {
name:'DongBei',
data(){
return {
name:'NEFU',
address:'哈尔滨',
}
},
methods:{
studentNameDeliver(name){
console.log('School组件已经接收到了来自Studennt组件的学生名称',name)
}
},
mounted() {
this.talkHelper.$on('studentName',this.studentNameDeliver)
},
beforeDestroy() {
this.talkHelper.$off('studentName')
}
}
</script>
<style >
.demo {
background-color: yellow;
}
</style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
总结
全局事件总线(GlobalEventBus)
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-
提供数据:
this.$bus.$emit('xxxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
文章来源: blog.csdn.net,作者:十八岁讨厌编程,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/zyb18507175502/article/details/125569062
- 点赞
- 收藏
- 关注作者
评论(0)