一、使用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组件传递信息。
2. 组件构造函数VueComponent与Vue.prototype的关系
在实现上述流程时,我们有必要知道这个重要的关系。
// 定义school组件
const school = Vue.extend({
name:'school',
...
})
// 组件的本质是VueComponent构造函数
console.log(school.protptype.__protp__ === Vue.prototype); // true
该图片来自尚硅谷天禹老师的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()去解绑当前组件绑定的事件。