一. props属性
用于父——>子组件通信。(当然,也可实现子——>父组件通信,但是很麻烦。需要父组件传给组件一个函数,子组件接收这个函数,然后子组件再给这个函数传值,调用这个函数。简单的说就是“子组件调用父组件传过来的函数”,感觉这并不是真正的“子——>父组件通信”)
1. 个人理解
- 相当于微信转账!一方发送数据,一方接收数据!
- 发送方需要使用“单项绑定v-bind”对要传送的数据进行绑定,要不然会只发送字符串
- 接收方有多种权力,通过配置可对传送类型进行限制,可限制是否必要性,可指定默认值
- props中接收的数据,不会存入data中,但是却比data中的数据先加载(优先级高)
- props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
2. 举例:
(1)例1:父——>子组件通信
发送方:
<template>
<div>
<Student name="李四" sex="女" :age="18"/>
</div>
</template>
接收方:
//简单声明接收
props:['name','age','sex']
//接收的同时对数据进行类型限制
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
}
}
(2)例2:子——>父组件通信
发送方(父组件):
<School :name="getName"/>
...
// js代码
methods:{
getName(name){ console.log('名称',name}
}
接收者(子组件):
//student.vue
<button @click="sendName">子组件调用父组件传过来的函数</button>
...
// js代码
data(){
return{
name:'小明'
}
},
props:['getName'],
methods:{
sendName(){
this.getName(this.name)
}
}
二. 自定义事件$on
、$emit
、$off
可实现子——>父通信
1. 个人理解
- 上述通过props也可实现子——>父通信,是子组件调用父组件传过来的方法,回调函数还是在父组件。但是这样非常麻烦,还需要使用props属性接收。而使用自定义事件实现子——>父通信就方便很多!两者有个共同点,那就是回调函数都是在父组件!
- 自定义事件理解:在父组件给子组件实例绑定自定义事件。然后子组件就可调用自己实例上的绑定的自定义事件。感觉大致还是和通过props属性实现子——>父通信差不多!
- 到底该如何理解自定义事件可实现子——>父通信?可通过父组件的函数调用子组件中的数据
- 如何在自定义组件上使用原生事件?不错,默认自定义组件认定原生事件为自定义事件,需要使用
native
关键字,例@click.native="show"
- 使用自定义事件完成子——>父通信有两种方式。一是通过@或v-on直接绑定自定义事件,二是通过指定
ref
属性和$on
或$once
来完成自定义事件绑定!两种方式都是在子组件中通过$emit
调用自定义事件! - 销毁了当前子组件的实例,销毁后所有Student实例的自定义事件全都不奏效,但是原生事件还是奏效!
$emit
:触发当前实例上的事件$on(eventName, listener)
: 绑定自定义事件监听(用于单独绑定事件,不是在组件上),需要给子组件标签上配置ref属性然后在mounted方法单独绑定$once(eventName, listener)
: 绑定事件监听, 但只能处理一次(用于单独绑定事件,不是在组件上),同上。$off(eventName)
: 解绑自定义事件监听,若不传参则解除绑定所有自定义事件
2. 举例
(1)例1:张天禹老师例子
父组件App:
<template>
<div class="app">
<h1>{{msg}},学生姓名是:{{studentName}}</h1>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
<!-- <Student @hello="getStudentName"/> -->
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) @click.native原生绑定测试-->
<Student ref="student" @click.native="show"/>
</div>
</template>
<script>
export default {
name:'App',
components:{School,Student},
methods: {
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
this.studentName = name
},
show(){
alert(123)
}
},
mounted() {
this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性)
},
}
</script>
子组件Student:
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>当前求和为:{{number}}</h2>
<button @click="add">点我number++</button>
<button @click="sendStudentlName">把学生名给App</button>
<button @click="unbind">解绑atguigu事件</button>
<button @click="death">销毁当前Student组件的实例(vc)</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'张三',
sex:'男',
number:0
}
},
methods: {
add(){
console.log('add回调被调用了')
this.number++
},
sendStudentlName(){
//触发Student组件实例身上的atguigu事件
this.$emit('atguigu',this.name,666,888,900)
},
unbind(){
this.$off('atguigu') //解绑一个自定义事件
// this.$off(['atguigu','demo']) //解绑多个自定义事件
// this.$off() //解绑所有的自定义事件
},
death(){
this.$destroy() //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效,原生事件add还是奏效。
}
},
}
</script>
三. 全局事件总线$bus
1. 定义
- 可用于任意组件间的通信
- 全局事件总线
$bus
并不是一种什么新的api技术,而是程序员把现有的知识总结用起来形成的通信方式,非常好用!称它为全局事件总线$bus
- 使用全局事件总线
$bus
需要配置两个要求:1是要求所有组件都能看见$bus
;2是要求全局事件总线$bus
能调用被调用者的$on
,$off
,$emit
。 $bus
是程序员共同默认的名字,当然写其他的也行!- 全局事件总线
$bus
,就是一个中间人,像是一个傀儡!!!也因此,它的压力非常大,所以需要使用befpreDestroy钩子函数,通过$off
去解绑当前组件所用到的事件!切记不用直接用$off()
不传参! - 为什么自定义事件中的组件没有强制要求要解绑自定义事件?vc组件用过自动销毁,销毁自动解绑所有自定义事件!而$bus是vm组件实例,不解绑的话压力多大可想而知!
2. 如何使用全局事件总线$bus?
(1)要求所有组件都能看见$bus
在main.js文件中,给vue实例对象上绑定$bus
数据
为什么?
三个选项:
1是window(可行,但是使用window不好!)
2是VueComponent实例对象(不可行,每个组件的VueComponent实例对象都不一样)
3是vue实例对象(这个只有一个,可行!!!)
例:Vue.prototype.$bus = {数据}
这里说是根据这个内置关系:VueComponent.prototype.__ proto__ = Vue.prototype
,但是这里我有点看不懂,先到这里!以后再看!!!
(2)要求全局事件总线$bus
能调用被调用者的$on
,$off
,$emit
等
- 谁才能调用
$on
,$off
,$emit
,Vue原型对象上才有这几个属性。 - vc原型对象最终还是调用vue原型对象,看下图。所以vc和vue(vm)原型对象都能调用**
$on
,$off
,$emit
等
1)vc形式:
2)vm形式:
安装全局事件总线
//创建vm
new Vue({
el:'#app',
render: h => h(App),
//看这里
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,也就是说全局事件总线$bus就是vm对象
},
})
如何使用?
使用也就和自定义事件通信差不多了,接收者通过$on
把事件绑定到$bus
上,发送数据者然后通过$emit
触发事件。
举例:
接收者:
mounted(){
this.$bus.$on('事件名',回调函数)
}
发送数据者:
this.$bus.$emit('事件名',数据)
四. 消息订阅与发布pubsub-js
即可子——>父,又可父——>子。但是用的少
五. 插槽
可实现父——>子组件通信
六. vuex
即可子——>父,又可父——>子。