目录
引入
父组件==>子组件
子组件==>父组件
全局事件总线
消息订阅与发布
引入
你知道Vue中组件之间应该如何进行通信吗?这里面就涉及到了多个关系了,父子之间互传、兄弟之间互传、子孙之间互传,甚至是任意的组件之间传递......
是不是感觉有点头皮发麻。没关系,本文将带领大家一起学习对应的解决方法!!!
父组件==>子组件
父组件若想传递信息给子组件,最常见的方式就是使用props配置项。该配置项的功能是让组件接受外部传过来的数据。
传递数据:<Demo name="xxx"/>
接收数据:
1.第一种方式(只接收):props:['name']
2.第二种方式(限制类型):props:{name:String}
3.第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
现在在脚手架中编写一个案例来实现父组件给子组件传递数据,并在页面上显示出来。默认的父组件为App, 子组件为Student。先从父组件传入三个数据给子组件,并且显示在页面上。接受方式分别采用以上的方式来实现,对应的代码如下:
App.vue
<template>
<div>
<!-- 为子组件传入数据 -->
<Student name="N-A" sex="男" age="5"/>
</div>
</template>
<script>
//导入Student组件
import Student from './components/Student'
export default {
name:'App',
//注册Student组件
components:{Student}
}
</script>
Student.vue
<template>
<div>
<h1>{{msg}}</h1>
<h2>博主姓名:{{name}}</h2>
<h2>博主性别:{{sex}}</h2>
<h2>博主年龄:{{age}}</h2>
</div>
</template>
<script>
export default {
//组件名命名为Student
name:'Student',
data() {
return {
msg:'我是一名CSDN博主'
}
},
//方式一:简单声明接收
props:['name','sex',"age"]
//方式二:接收的同时对数据进行类型限制
props:{
name:String,
age:Number,
sex:String
}
//方式三:接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, //name是必要的
},
age:{
type:Number,
default:99 //默认值
},
sex:{
type:String,
required:true
}
}
}
</script>
分别运行以上代码提供的三种方式运行的效果图如上,三种方式都能够让数据在页面上进行显示。但是其中涉及到一些问题。图二为采用方式二来接收传入的数据时的控制台出现的错误,虽然能够在页面上看到数据,但是已经存在问题了,因为我们在接收数据时规定了age属性传入的数据的类型为number类型。
但是我们在传值时,编写了age="5", 因此传入的5则为字符串。那该如何解决呢?只需要在age前面加上冒号。对其进行绑定,绑定之后双引号之中的东西都会当成是JS的表达式去解析,因此,解析之后就是纯粹的数字5,因此就能够解决相应的问题。
现在我在增加一个要求,在页面上增加一个按钮通过点击之后来实现页面上的年龄加一的效果。对应代码如下:
<template>
<div>
<h1>{{msg}}</h1>
<h2>博主姓名:{{name}}</h2>
<h2>博主性别:{{sex}}</h2>
<h2>博主年龄:{{MyAge}}</h2>
<button @click="updateAge">年龄加一</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
msg:'我是一名CSDN博主',
MyAge:this.age
}
},
methods: {
updateAge(){
this.MyAge++
}
},
//简单声明接收
props:['name','age','sex']
}
</script>
效果能够正常显示。(在线请教各位小伙伴你们文章中的动图是怎样制作的。)看了以上的代码可能会有小伙伴会问,为啥子组件接收到的age数据不直接修改呢,直接修改也能够实现相应的效果。为啥还需要另一个设置MyAge来接收它,再修改MyAge的值呢?这时候就需要注意一下以下的注意事项了。
注意:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
子组件==>父组件
实现方法一:通过父组件给子组件传递函数类型的props,从而实现子组件给父组件传递数据。在前面我们已经对props有了一定的了解了,那么现在我们就来通过代码实现吧!
案例:School组件有学校名以及学校地址两个信息,现在需要实现App父组件可以接收到School子组件传过来的学校名。
App.vue(父组件)
<template>
<div class="app">
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components:{School},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
}
}
}
</script>
<style scoped>
.app{
background-color: gray;
padding: 5px;
}
</style>
在App父组件中,定义了一个getSchoolName函数,并为子组件传递了该函数。
School.vue(子组件)
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
name:'School',
props:['getSchoolName'],
data() {
return {
name:'史莱克学院',
address:'天斗帝国',
}
},
methods: {
sendSchoolName(){
this.getSchoolName(this.name)
}
},
}
</script>
<style scoped>
.school{
background-color:rgb(212, 237, 210);
padding: 5px;
}
</style>
子组件使用props配置项接收了getSchoolName函数,在sendSchoolName函数中调用传过来的函数,并传入学校名。并为按钮绑定getSchoolName点击事件。通过点击之后来实现向父组件传递学校名的目的。对应的实现效果如下:
实现方法二:若子组件想要传数据给父组件,还可以使用组件的自定义事件。使用场景为:若A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
绑定自定义事件有如下的两种方式:
第一种方式,在父组件中:<Demo @自定义事件名="test"/>
或 <Demo v-on:自定义事件名="test"/>
案例还是上面的,通过修改第一种方式的自定义事件对应的代码如下:
App.vue(父组件)
<template>
<div class="app">
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
<School @showName="getSchoolName"/>
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components:{School},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
}
},
}
</script>
<style scoped>
.app{
background-color: gray;
padding: 5px;
}
</style>
School.vue(子组件)
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'史莱克学院',
address:'天斗帝国',
}
},
methods: {
sendSchoolName(){
//触发School组件实例身上的showName事件
this.$emit('showName',this.name)
}
},
}
</script>
<style scoped>
.school{
background-color:rgb(212, 237, 210);
padding: 5px;
}
</style>
实现效果如下:能够正常的显示出来!
第二种方式,只是在父组件中换一种绑定事件的方式,只需要修改父组件中代码:
<template>
<div class="app">
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
<School ref="school" />
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components:{School},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
}
},
mounted() {
this.$refs.school.$on('showName',this.getSchoolName) //绑定自定义事件
},
}
</script>
<style scoped>
.app{
background-color: gray;
padding: 5px;
}
</style>
实现效果与之上的相同。关于自定义事件的其他知识点,我就不在这里过多赘述,毕竟主要是分享它如何实现子组件向父组件传数据。
注意:通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
全局事件总线
全局事件总线是一种组件间通信的方式,适用于任意组件间通信。
1.安装全局事件总线:
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
2.使用事件总线:
- 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
- 提供数据:
this.$bus.$emit('xxxx',数据)
3.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
案例:现有两个兄弟组件,分别是Student组件以及School组件,要实现Student组件给School组件传送数据。对应的实现代码如下:
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() {
Vue.prototype.$bus = this
}
})
提供数据方组件代码如下:
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'N-A',
sex:'男',
}
},
methods: {
//提供数据
sendStudentName(){
this.$bus.$emit('hello',this.name)
}
},
}
</script>
<style scoped>
.student{
background-color:rgb(212, 237, 210);
padding: 5px;
margin-top: 30px;
}
</style>
接收数据方组件代码如下:
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'史莱克学院',
address:'天斗帝国',
}
},
//绑定一个hello事件,用于接收数据
mounted() {
this.$bus.$on('hello',(data)=>{
console.log('我是School组件,收到了数据',data)
})
},
//用$off去解绑<span style="color:red">当前组件所用到的</span>事件。
beforeDestroy() {
this.$bus.$off('hello')
},
}
</script>
<style scoped>
.school{
background-color:rgb(212, 237, 210);
padding: 5px;
}
</style>
实现效果如下,兄弟组件之间实现了数据的传递。
消息订阅与发布
消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信。使用方式如下:
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)
去取消订阅。
该方式与上述的全局事件总线比较相似,主要是不需要用到$bus,而是替换成了pubsub。
对以上的代码进行修改之后如下:
提供数据方组件代码如下:
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'Student',
data() {
return {
name:'N-A',
sex:'男',
}
},
methods: {
sendStudentName(){
pubsub.publish('hello',this.name)
}
},
}
</script>
<style scoped>
.student{
background-color:rgb(212, 237, 210);
padding: 5px;
margin-top: 30px;
}
</style>
接收数据方组件代码如下:
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'School',
data() {
return {
name:'史莱克学院',
address:'天斗帝国',
}
},
mounted() {
this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
console.log(data)
})
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId)
},
}
</script>
<style scoped>
.school{
background-color:rgb(212, 237, 210);
padding: 5px;
}
</style>
实现效果如下:
好啦,本文就到这里结束了,你学到了吗?若有任何的疑问或者问题欢迎各位在评论区或者私信找我一起交流学习!