文章目录
前言
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、自定义事件总结
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
-
绑定自定义事件:
-
第一种方式,在父组件中:
<Demo @xxxx="test"/>
或<Demo v-on:xxxx="test"/>
-
第二种方式,在父组件中:
<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on('xxxx',this.test) }
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。
-
-
触发自定义事件:
this.$emit('xxxx',数据)
-
解绑自定义事件
this.$off('xxxx')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
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、全局事件总线总结
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... })
-
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){ demo(data){ ......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) }
-
提供数据:
this.$bus.$emit('xxxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
4、消息订阅与发布
使用消息订阅与发布需要安装相应的包,如今有很多消息订阅与发布的包,在这里只介绍一种,它的使用与全局事件总线几乎一样。
-
一种组件间通信的方式,适用于任意组件间通信。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){ ......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }
-
提供数据:
pubsub.publish('xxx',数据)
-
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅。
-