前端vue入门(纯代码)11

11.全局事件总线(Global Event Bus)

全局事件总线:实现任意组件间通信

【原理图】

在这里插入图片描述

  • 结合上图,假若C组件想要传递数据给A组件,那么,就通过全局事件总线在A组件中绑定【$on】一个自定义事件demo,并界定一个参数来接收传递的数据,同样在C组件中,就需要通过全局事件总线对自定义事件demo进行触发【$emit】,并传递参数,这样就实现了任意组件之间的通信。

  • 简单理解,全局事件总线其实就是一个中间介质,组件间的相互通信借助于这个中间介质,通过这个中间转换介质,从而完成数据的传递与接收,实现组件间的相互通信。

  • 全局事件总线是一个独立存在的部分,要想实现组件间的相互通信,又是自定义事件,那就要满足两个条件:

    【1】. 所有的组件都能访问到全局事件总线【X总线】

    【2】. 可以调用$on【绑定】,$off【解绑】,$emit【触发事件】


全局事件总线应该安装在哪里呢?

  • 安装全局事件总线前需要考虑:如果要所有组件对象【vm,vc】都能够获取到事件总线
  • 从下图可以看出,当组件实例化对象vc获取数据时,会首先找到组件原型对象,如果该组件实例对象中没有此数据,那么就找到Vue的原型对象,Vue的原型对象只有一个,所有组件都能够获取到它的数据,所以事件总线要安装到Vue的原型对象上

在这里插入图片描述

结合组件的内置关系:

VueComponent.prototype.__proto__ === Vue.prototype【Vue的原型对象】

这个关系在作用就在于可以让 组件实例对象(vc,vm) 可以访问到Vue原型对象【Vue.prototype】上的属性和方法

  • 回顾知识:关于VueComponent:

    1.A、B、C子组件本质上是一个名为VueComponent的构造函数,且不是程序员定义的,是调用Vue.extend()生成的

    2.只需要写<A/>或<A></A>,Vue解析时会帮我创建A组件的实例对象,

    3.每次调用Vue.extend,返回的都是一个全新的VueComponent

  • 1. 由于是在入口文件main.js中引入的Vue,所以事件总线需要配置到main.js当中

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
  // 生命周期钩子beforeCreate中模板未解析,且this是vm
  beforeCreate() {
		Vue.prototype.$bus = this  //安装全局事件总线$bus
	}
})
  • 为什么要在Vue实例对象中采用生命周期钩子beforeCreate安装事件总线?
    • 由生命周期beforeCreate此时只是完成了初始化工作,创建了Vue实例对象,但是模板并没有解析,数据代理并没有运转。
  • 2. 接下来,我们就要对想要接收到数据的组件进行自定义事件的绑定,简单来说就是,谁【比如:A】要接收数据,自定义事件就绑定在谁【比如:A】身上 【$on()里面要是用箭头函数】
  • 绑定全局事件总线

此处若用的是箭头函数,不是普通函数,那么函数里的this指向【vc】不会有问题。

mounted(){
  // 绑定自定义事件
  //此处若用的是普通函数,不是箭头函数,那么函数里的this指向会有问题。
  this.$bus.$on('自定义事件名', (接收参数)=>{
    console.log('我是TestB组件,收到了数据', 接收参数);
    console.log('School组件里的this', this);  //vc
  })
}

此处若用的是普通函数,不是箭头函数,那么函数里的this指向【vm】会有问题。

mounted(){
  // 绑定自定义事件
  //此处若用的是普通函数,不是箭头函数,那么函数里的this指向会有问题。
  this.$bus.$on('自定义事件名',function (接收参数){
    console.log('我是TestB组件,收到了数据', 接收参数);
    console.log('School组件里的this', this);  //vm
	console.log('this.$bus', this.$bus);  //vm
  })
}
  • 3. 最后一步,全局事件总线的触发,事件的触发是在发送数据的组件中完成的,简单来说,谁【比如:B】是数据的发送者,谁【比如:B】就来触发事件
  • 触发全局事件总线
 methods:{
  // 触发事件,事件名不能重复
  触发事件方法名(){
    this.$bus.$emit('自定义事件名', 传递参数);
  }
},
  • 4. 在得到数据之后,解绑事件,提高性能,在beforedestroy生命周期钩子中,用$off解绑当前组件所用到的数据
  • 解绑自定义事件
// 销毁对应自定义事件
beforeDestroy(){
  this.$bus.$off('自定义事件名')
}

完整代码:

School.vue组件

<template>
	<div class="school">
		<h1>School组件</h1>
		<!-- 给子组件的实例对象VC绑定了事件getName,该事件触发会调用函数getStudentName -->
		<h2>兄弟组件Student传过来的name:{{ StudentName }}</h2>
		<h2>兄弟组件Student传过来的age:{{ StudentAge }}</h2>
	</div>
</template>

<script>
export default {
	name: 'School',
	data() {
		return {
			StudentName: '',
			StudentAge: '',
		};
	},
	mounted() {
		// 绑定自定义事件haha
		this.$bus.$on('haha', (...params) => {
			console.log('School组件里的', this);
			this.StudentName = params[0];
			this.StudentAge = params[1];
			console.log('我是School组件,收到了数据', params);
		});
	},
  // 销毁对应自定义事件
	beforeDestroy() {
    // 解绑自定义事件
    this.$bus.$off('haha')
  },
};
</script>

<style scoped>
.school {
	background-color: rgb(73, 192, 150);
}
</style>

  • ...params:可以以数组的形式接收多个参数。

Student.vue组件

<template>
	<div class="student">
    <h1>Student组件信息</h1>
		<h2>学生姓名:{{ name }}</h2>
		<h2>学生性别:{{ sex }}</h2>
		<h2>学生年龄:{{ age }}</h2>
		<h2>学生成绩:{{ score }}</h2>
		<button class="haha" @click="sendStudentlName">
      <h2>点击此处给兄弟组件School传值</h2></button>
	</div>
</template>

<script>
export default {
	name: 'Student',
	data() {
		return {
			name: '何大春',
			sex: '男',
			age: '22',
			score: '88',
		};
	},
	methods: {
    sendStudentlName(){
      console.log("Student组件里的",this.name);
      // 传给School组件两个参数
      this.$bus.$emit('haha',this.name,this.age)
    }
	},
};
</script>

<style lang="less" scoped>
.student {
	background-color: tomato;
	padding: 50px;
	margin-top: 50px;
	margin-left: 50px;
	width: 300px;
	height: 350px;
}
.h2 {
  padding: 5px;
  margin: 5px 5px 5px 5px;
}
.haha {
  background-color: rgb(211, 233, 130);
}
</style>

结果展示:

在这里插入图片描述

补充知识点:

问题1:“全局事件总线”需要哪些特点?

  • 1)被所有组件(vc、vm)能够看得见
  • 2)能够调用$on、$emit、$off

问题2:“Vue原型对象----Vue.prototype上面的所有属性和方法是给谁用的?

  • 是给所有的vm和vc使用的

问题3:为什么定义“全局事件总线”要放在main.js文件中?

  • 因为哪里引入Vue,哪里才会去定义“全局事件总线”

问题4:“为什么定义“全局事件总线”要放在beforeCreate的钩子函数中?

  • 1)beforeCreate钩子函数里this指代new出来的vm

  • 2)在beforeCreate钩子函数里模板还没解析,数据监测和数据代理也还没完成呢。也就是说借助这个beforeCreate钩子函数你把想做的事儿做好了,原型上该放的放好了,随后模板开始解析,等组件执行的时候你该放的都放好了,后续才都不会产生影响。

问题5:如何避免在使用“全局事件总线”时自定义函数名重名使用问题?比如组件1使用自定义函数名叫demo,那组件2不全文搜索也使用了自定义函数名也叫demo,这就混了

  • 真实项目中src目录下创建一个config文件夹,里面创建个constants常量文件,里面用来定义要使用的自定义函数名,方便别人查看并避免重名问题。

问题6:“为什么要在组件销毁之前,把“全局事件总线”中定义的自定义事件函数解绑?那“知识点组件自定义事件”中咋没说解绑的事儿呢?

  • “组件自定义事件”中,组件销毁了== vc销毁了,vc销毁了自定义事件也就销毁了,而“全局事件总线”中定义的自定义事件是一直存在的,哪怕使用组件销毁了,但是Vue实例定义的“全局事件总线”中还是会存在自定义事件,所以需要在组件销毁之前进行解绑。

注意7:销毁“全局事件总线”中定义的自定义事件请放在beforeDestroy()钩子中

注意8:子组件中使用“全局事件总线”时this.$bus.$on()中回调配置要使用箭头函数,不要使用普通函数,普通函数中this指代vue实例,而箭头函数中this才指代vc,因为最终要在school组件上接收平行组件发过来的消息,所以要使用vc,而不是要使用vue实例,因为vue实例不是我们最终要的。

mounted(){
  // 绑定自定义事件
  this.$bus.$on('自定义事件名', (接收参数)=>{
    console.log('我是TestB组件,收到了数据', 接收参数);
  })
},
	mounted() {
		// 绑定自定义事件haha
		this.$bus.$on('haha', (...params) => {
			console.log('School组件里的', this); //this = vc
			this.StudentName = params[0];
			this.StudentAge = params[1];
			console.log('我是School组件,收到了数据', params);
		});
	},
总结:

全局事件总线实现了任意组件间的通信,有效简化了开发过程中一些数据传递的操作,同时也提高了程序的性能,但是值得注意的是,全局事件总线之所以叫全局,是因为任何组件都可以访问,这就导致如果大量组件都绑定了全局事件总线,难免会造成代码混乱,且自定义事件名可能发生重复的问题,所以在开发中,使用全局事件总线时要根据实际业务情况进行选择。

  1. 全局事件总线:一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

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

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    2. 触发事件+提供数据:this.$bus.$emit('xxxx',数据)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值