• 全局事件总线


    原理分析

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

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

  • 相关阅读:
    二叉树层次遍历
    [运维|数据库] MySQL中的longtext类型在PostgreSQL中用text表示
    [C++](21)set和map的模拟实现
    项目管理之实施关键步骤
    心理月刊杂志心理月刊杂志社心理月刊编辑部2022年第11期目录
    服务器和客户端的简单交互
    Python tkinter - 第8章 单选按钮控件(Radiobutton)属性
    Java JavaMail通过SMPT发送邮件
    第十三届蓝桥杯省赛JavaA组 H 题,JavaC组 I 题—— 因数平分和 (AC)
    数仓开发之DWS层(二)
  • 原文地址:https://blog.csdn.net/zyb18507175502/article/details/125569062