全局事件总线
作用:可以在全局层面上,在任意组件之间相互传递数据。不再局限于父子组件传值,或多层嵌套传值等方式。
使用方式:完全与父子组件传值一致,使用 $on 监听事件,使用 $emit 触发事件,使用 $off 解绑事件。
理解
1、事件总线相当于是独立于所有组件之外的旁观者,不参与组件内的任何逻辑,只负责调用 Vue 实例对象上的方法,且所有组件都能访问到它
2、既然所有组件都能使用事件总线,那我们可以将其定义在两个地方
window:直接挂载到 window 全局对象上( 但是会导致污染window 全局对象,可能造成属性混乱 ),且通过 window.x 不能访问到 Vue 实例对象上的 $on 等方法。直接排除
window.x = {a:1,b:2}Vue 原型对象:挂载到 Vue.prototype 上,组件实例对象 和 Vue实例对象都能访问到该属性。
Vue.prototype.x = { a: 1, b: 2 };3、在 Vue 原型对象,以及 组件实例对象上都是不存在这个东西的,但是我们又需要使用 Vue 实例对象上的 $on、$emit、$off 等方法,所以这玩意应该继承了 Vue 原型对象,或者 组件实例对象
4、继承 组件实例对象 ,或 Vue 原型对象 ( 在 main.js 中挂载 )
继承 VueComponent组件实例对象
// 通过 Vue.extend({}) 返回 VueComponent 构造函数
const Demo = Vue.extend({})
// 通过 new Demo() 得到 VueComponent 构造函数 的实例对象
const d = new Demo()
// 将 VueComponent 构造函数 的实例对象 挂载到 Vue.prototype 实例对象上
Vue.prototype.x = d;继承 Vue 实例对象:
// 如果这样写,那肯定是错误的,因为 new Vue() 已经完成,且挂载了,这个时候再去向 Vue.prototype 添加属性会报错
const vm = new Vue({
  render: (h) => h(App),
}).$mount("#app");
Vue.prototype.x = vm;使用 beforeCreate 生命周期钩子函数,在 Vue 实例创建之前,向 Vue.prototype 中添加属性。
new Vue({
  render: (h) => h(App),
  beforeCreate() {
    // 当前 this 就是 new Vue() 实例,这是 Vue 底层设计,在生命周期钩子函数中,this都是指向当前 Vue实例对象 或 组件实例对象
    Vue.prototype.x = this
  }
}).$mount("#app");继承 组件实例对象 ,或 Vue 原型对象 都可以使的 事件总线对象访问到 $on、$emit 等Vue 底层的方法。但是看写法来说,肯定是使用 beforeCreate 生命周期钩子函数好啊
用法
因为 上面已经 通过 beforeCreate 生命周期钩子函数 使得 事件总线对象 继承了 底层方法,所以下面可以直接使用
定义 School 子组件。在 mounted 钩子函数中,注册了 hello 事件,且进行监听
<template>
  <div class="demo">
    <p>School子组件</p>
  </div>
</template>
<script>
export default {
  mounted() {
    this.x.$on('hello', (data) => {
      console.log('我接受到了数据',data)
    })
  },
};
</script>定义 Student 子组件。点击 test 之后 触发 hello 事件,传递数据给 School 子组件
<template>
  <div class="demo">
    <p @click="test">Student子组件</p>
  </div>
</template>
<script>
export default {
  methods: {
    test() {
      this.x.$emit('hello','123')
    },  
  },
};
</script>定义App 组件,
<template>
  <div id="app">
    <School/>
    <Student />
  </div>
</template>两个兄弟组件之间没有任何关联,但是现在需要 School 子组件 获取到 Student 子组件 传递的数据,这个时候,我们的事件总线就起到作用了。
点击 test 事件,触发 hello 事件,且传递数据。此时控制台上打印出了 Student 传递出的数据。

这个 this.x 只是我随便定义的一个属性,不是固定名称,建议使用 $bus ,类似于 Vue 底层的 $on、$emit 等方法。
第二种方法:需要新建文件,且使用时每次需要引入文件,较麻烦。只不过一个是在原型上增加属性,一个是直接使用全新的 Vue 实例对象
新建 js 文件:直接暴露 Vue 实例对象
//bus.js
import Vue from 'vue';
export default new Vue();
引入文件:相当于引入了一个 全新的 Vue 实例对象
import Bus from '@/module/util/bus';使用:和父子组件传值一样,$on 监听事件 $emit 触发事件
// 监听事件
Bus.$on('getLevelList', data => {
    this.levelList = data;
});
// 触发事件
Bus.$emit('getLevelList', res.data);建议
如果组件被销毁,建议解绑该组件上的事件,因为事件总线上 监听的事件不会随着组件的销毁而自动解绑,这样可能会造成 事件总线上绑定了过多的 不用监听的事件
beforeDestroy() {
  this.$bus.$off('hello')
},总结
全局事件总线:
一种组件间的通信方式,适用于任意组件通信( 父子组件,兄弟组件,深层嵌套组件 )
挂载全局事件总线:建议使用该方法,不会增加额外代码,且使用方便
new Vue({
  render: (h) => h(App),
  beforeCreate() {
    Vue.prototype.x = this
  }
}).$mount("#app");使用全局事件总线
// 监听事件
this.$bus.$on('hello', (data) => {
  console.log('我接受到了数据',data)
})
// 触发事件,传递数据
test() {
  this.x.$emit('hello','我传递了数据')
},组件销毁之前解绑事件,避免事件总线上绑定过多无用的事件
beforeDestroy() {
  this.$bus.$off('hello')
},


















