全局事件总线

举报
十八岁讨厌编程 发表于 2022/08/05 22:55:55 2022/08/05
【摘要】 文章目录 原理分析代码实现代码优化总结 原理分析 全局事件总线能实现任意两个组件间的通信 例如我们现在有如下的结构: 我们想要实现任意两组件的通信,少不了一个中介X(图中右上角的粉色圆...

原理分析

全局事件总线能实现任意两个组件间的通信

例如我们现在有如下的结构:
在这里插入图片描述
我们想要实现任意两组件的通信,少不了一个中介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)

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

    new Vue({
    	......
    	beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    	},
        ......
    }) 
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
            
           
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

文章来源: blog.csdn.net,作者:十八岁讨厌编程,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/zyb18507175502/article/details/125569062

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。