关于Vue的组件间通信

757 阅读2分钟

一、使用props配置项

props功能:让组件接收外部传过来的数据。

props接收到的数据最终会在组件实例对象上。

1. props实现父向子传递数据

(1) 传递数据:

父组件写子组件标签时,可以在标签内用如下方式传递参数给子组件。

<Demo name="xxx">

(2) 接收数据:

第一种方式(只接收):

props:['name']

第二种方式(限制类型):

prosps: {
    name:String
}

第三种方式(限制类型、限制必要性、指定默认值):

props:{
    name:{
        type:String,     // 类型
        required:true,   // 必要性
        default:'小磊'   // 默认值
    }
}

备注:props接收的数据是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的数据内容到data中一份,然后去修改data中的数据。

2. porps也可以实现子向父传递数据

我们可以在父组件中事先定义一个方法,这个方法可以接收参数。

data() {
    return {
        nameArr: ['小王', '小张']
    }
}
methods: {
    demo(name) {
        this.nameArr.push(name); 
    }
}

然后在子组件中通过props接收到父组件定义的方法,在合适的时候去调用它,并传入参数,于是就可以实现子组件向父组件传递数据。

二、使用自定义事件实现子组件向父组件传值

1. 什么是组件的自定义事件

(1) 就像原生的click事件、keyup事件,我们可以在组件标签上绑定我们自定义的事件。

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

2. 绑定自定义事件

(1) 第一种方式,在父组件中通过 @ 或 v-on 绑定自定义事件:

<Demo @xiaolei="test"/><Demo v-on:xiaolei="test">

(2) 第二种方式,在父组件中通过ref标识子组件,获取到子组件的实例对象,通过$on绑定自定义事件

<Demo ref="demo"/>
.......
mounted() {
    // 给子组件Demo绑定自定义事件xialei,事件的回调是父组件上的test方法
    this.$refs.demo.$on('xiaolei', this.test);//
}

(3) 如果希望自定义事件只出发一次,可以用once修饰符或者$once方法。

3. 子组件中触发以及解绑自定义事件

(1) 触发自定义事件可以使用$emit方法,数据同样以参数的形式传递给父组件的回调函数

this.$emit('xiaolei', 数据)

(2) 解绑自定义事件

this.$off('xiaolei')

注意: 通过this.$ref.xxx.$on('xiaolei', 回调) 绑定自定义事件时,回调函数要么配置在methos中,要么使用箭头函数,否则函数this的指向会出问题!(因为给子组件绑定事件,回调使用普通函数function(){}指定,this会指向事件的触发者,也就是子组件实例对象)。

三、全局事件总线 (GlobalEventBus)--任意组件间通信

通过上述的两种方式,我们只能实现父子组件间通信,却很难实现兄弟组件,或者无关系的两个组件间通信。

1. 通过自定义事件思考

通过自定义事件,我们发现,只要A组件给一个组件绑定了自定义事件,当这个事件被触发时,A组件中的回调就会被执行,于是可以通过回调函数的参数接收到传递过来的数据。

于是我们设想如果有一个完全独立的组件X,通过A组件去给它绑定自定义事件,事件的回调自然在A组件里,当B组件需要给A组件传递参数时,通过B组件去触发组件X内A绑定的自定义事件并传递参数,于是便可以实现B组件给A组件传递信息。

全局事件.png

2. 组件构造函数VueComponent与Vue.prototype的关系

在实现上述流程时,我们有必要知道这个重要的关系。

// 定义school组件
const school = Vue.extend({
    name:'school',
    ...
})
​
// 组件的本质是VueComponent构造函数
console.log(school.protptype.__protp__ === Vue.prototype); // true

分析Vue与VueComponent的关系.png

该图片来自尚硅谷天禹老师的Vue课程

由此可知,原本应该指向Object的原型的组件构造函数,被Vue修改并且指向了Vue的原型。从而,由组件构造函数创建的组件,都可以直接访问Vue原型上的属性和方法。

3. 实现全局事件总线

如果要实现第一点中的X,那么首先这个X要被任意组件都能访问到,并且X需要有$on$emit方法,因为任何组件都需要能给X绑定自定义事件或者触发它身上的事件。

此时这个X最佳定义的位置便是Vue.prototype,因为Vue原型上的属性和方法,不论是Vue实例还是组件实例,都可以直接访问到。

于是,我们可以在创建Vue实例时,在Vue原型上定义一个属性$bus指向这个实例,于是,任何组件都可以在Vue实例上定义事件,也可以触发它身上的事件。这便是全局事件总线。

(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', 数据)

注意: 因为每一个组件都可能通过Vue实例绑定事件,所以当组件销毁时,最好在beforeDestroy钩子中,使用$off()去解绑当前组件绑定的事件。