Vue组件间通信(props 自定义事件 全局事件总线 消息订阅与发布)

前言

vue的组件化使得可以将完成一种特定功能的代码和资源分离出来,提高了复用性。但随之也产生了一些问题,组件的这种隔离性使得组件间的数据交换变得更为繁琐,因此实现组件间的通信是十分重要的。下面我就结合自己所学,整理了几种适用于组件间通信的方式。

1、使用props在父子组件中通信

我的前一篇博客中详细介绍了父组件使用props给子组件传递数据的方式,详情请点击Vue组件化:(ref, props, mixin, 插件)。但这样也仅仅局限于父组件给子组件传递数据,那我们如何通过props实现子组件给父组件传递数据呢?

我们可以给子组件传递一个函数,函数里携带父组件的数据,在子组件中接收这个函数,通过调用这个函数,修改里面的参数我们可以实现子组件给父组件传递数据。

在todolist案例

App组件中,MyFooter组件传递一个函数clearAllTodo,用于删除所有已完成的事项

<MyFooter :todos="todos" :clearAllTodo="clearAllTodo"/>
clearAllTodo(){
    
    
	this.todos = this.todos.filter((todo)=>{
    
    
		return !todo.done
	})
}

MyFooter组件中接收这个函数

props:['todos','clearAllTodo'],
clearAll(){
    
    
	this.clearAllTodo()
}

这样就能在子组件中对父组件的数据进行处理,虽然这种方式比较繁琐,但在没有接触自定义事件以及全局事件总线,消息订阅与发布之前,是一种可行的方法。

2、自定义事件

实现组件间通信还可以使用自定义事件来解决,使用自定义事件要完成两步的工作

  • 绑定自定义事件
  • 在合适的时机触发自定义事件

在这里插入图片描述

2.1、自定义事件的第一种实现方式

以将学生名传递给App组件为例,在App组件中给Student组件绑定自定义事件,并配上事件的回调。

<template>
  <div class="app">
    <h1>{
   
   { msg }},学生姓名是:{
   
   { studentName }}</h1>
    <School :getSchoolName="getSchoolName"/>
    <Student @receive="getStudentName"/>
  </div>
</template>
//...params表示可以收到数量不等的参数,以对象的形式
getStudentName(name, ...params) {
    
    
  console.log('App收到了学生名:', name, params)
  this.studentName = name
},

Student组件中触发这个自定义事件,并携带学生名

    <button @click="sendStudentName">把学生名发送给App</button>
    sendStudentName() {
    
    
      this.$emit('receive', this.name, this.sex, this.number)
      console.log('receive事件触发了')
    },

在这里插入图片描述
结果如图所示,父组件收到了子组件传递的数据。

2.2、自定义事件的第二种实现方式

在第一种实现方式是在标签体内部直接给子组件绑定自定义事件,其实还可以通过ref属性,获取子组件,绑定自定义事件。

<template>
  <div class="app">
    <h1>{
   
   { msg }},学生姓名是:{
   
   { studentName }}</h1>
    <School :getSchoolName="getSchoolName"/>
    <Student ref="student"/>
  </div>
</template>

我们调用一个生命周期钩子,完成绑定事件,

  mounted() {
    
    
    console.log('App收到了学生名:',)
    this.$refs.student.$on('receive', this.getStudentName)
  }

在这里插入图片描述
依然有效。

2.3、解绑自定义事件

在我之前讲述vue生命周期的博客中:一篇文章带你搞懂Vue的生命周期,讲述了在mounted时期绑定自定义事件,在beforeDestroy阶段解绑自定义事件,事实上我们也可以手动解绑自定义事件。解绑自定义事件需要用到$off这个API。

this.$off('receive')//解绑一个
this.$off(['receive',...])//解绑多个
this.$off()//解绑所有

2.4、自定义事件总结

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @xxxx="test"/><Demo v-on:xxxx="test"/>

    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
              
              
         this.$refs.xxx.$on('xxxx',this.test)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('xxxx',数据)

  5. 解绑自定义事件this.$off('xxxx')

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  7. 注意:通过this.$refs.xxx.$on('xxxx',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

3、全局事件总线

全局事件总线给我们提供了另外一种组件通信的方式,它适用于任意间的组件间通信。

注意:全局事件总线并不是一种新的技术,我们甚至不会用到新的API,它是由程序员根据多年经验总结出来的一套最佳实践。

3.1、全局事件总线的原理

  • 全局事件总线是是连接所有组件的媒介,或者说它能被所有组件的实例对象所看到。
  • 全局事件总线使用了自定义事件,因此,全局事件总线可以使用$emit $on $off等API。
  • 全局事件总线要能够包含vue中的所有属性,以便实现任意组件之间共享数据。

因此,我们可以在vue的原型对象上添加这么一个属性,满足上面的条件,于是就形成了下面的代码:

//创建vm
new Vue({
    
    
	el:'#app',
	render: h => h(App),
	beforeCreate() {
    
    
		Vue.prototype.$bus = this //安装全局事件总线
	},
})

注意:$bus是我们自己定义的,vue中并没有这个属性,而且bus本身就具有总线的意思。

3.2、案例分析

依然是上面的案例,使用全局事件总线实现

Student组件

<template>
	<div class="student">
		<h2>学生姓名:{
   
   {name}}</h2>
		<h2>学生性别:{
   
   {sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

触发自定义事件

methods: {
    
    
	sendStudentName(){
    
    
		this.$bus.$emit('hello',this.name)
	}
},

School组件

<template>
	<div class="school">
		<h2>学校名称:{
   
   {name}}</h2>
		<h2>学校地址:{
   
   {address}}</h2>
	</div>
</template>

绑定自定义事件

mounted() {
    
    
	// console.log('School',this)
	this.$bus.$on('hello',(data)=>{
    
    
		console.log('我是School组件,收到了数据',data)
	})
},

在这里插入图片描述
也实现了组件间的通信,并且是兄弟组件间的通信。

最后不要忘了解绑自定义事件。

beforeDestroy() {
    
    
	this.$bus.$off('hello')
},

3.3、全局事件总线总结

  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去解绑当前组件所用到的事件。

4、消息订阅与发布

使用消息订阅与发布需要安装相应的包,如今有很多消息订阅与发布的包,在这里只介绍一种,它的使用与全局事件总线几乎一样。

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

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){
              
              
        demo(data){
              
              ......}
      }
      ......
      mounted() {
              
              
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

猜你喜欢

转载自blog.csdn.net/weixin_51624761/article/details/124712685