全局事件总线
作用:可以在全局层面上,在任意组件之间相互传递数据。不再局限于父子组件传值,或多层嵌套传值等方式。
使用方式:完全与父子组件传值一致,使用 $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 事件,且进行监听
School子组件
定义 Student 子组件。点击 test 之后 触发 hello 事件,传递数据给 School 子组件
Student子组件
定义App 组件,
两个兄弟组件之间没有任何关联,但是现在需要 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')
},