组件的自定义事件
1.一种组件间通信方式,适用于:子组件=>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
3.绑定自定义事件:
- 第一种方式在父组件中:
<student @xiaomi="demo"/>
或者<student v-on:xiaomi="demo"/>
- 第二种方式在父组件中:
<student ref="demo"/>
.....
mounted(){
this.$ref.xxx.$on('xiaomi',this.name
}
- 若想让自定义事件只能触发一次,可以使用once修饰符,或
$once
方法
4.触发自定义事件:this.$emit('xiaomi',数据)
5.解绑自定义事件:this.$off('xiaomi')
6.组件上也可以绑定原生DOM事件,需要使用native修饰符
7.注意:通过this.$ref.xxx.$on('xiaomi',回调}
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题
一.props实现传递数据
通过父组件给子组件传递函数类型的props实现:子给父传递数据
实现:点击按钮将学校名交给App,实现子组件给父组件传递数据
分析:
1.App组件想要得到school组件的学校名就需要在自己的组件里表达出想要的意思(定义得到学校名字的方法)
2.表达出想要的意思还不行,还要告诉他我已经定义了方法你接受下(将方法给到school)
3.之后就用props声明接收,整个getSchoolName我们定义完,就可以直接定义按钮来用它
App.vue
- 定义方法(首先在app.vue里面定义得到学校名字的方法)
script>
// 引入组件
import School from './components/School.vue'
import Student from './components/Student.vue'
export default {
name:'App',
components:{School,Student},
data() {
return {
msg:'你好啊'
}
},
// 首先先定义方法
methods: {
getSchoolName(name){
console.log('App收到了学校名',name);
}
},
}
- 之后在App.vue里面将方法给到school组件
<template>
<div class="app">
<h1>{{msg}}</h1>
<!-- 将这个方法给到school -->
<School :getSchoolName = "getSchoolName"/>
<Student/>
</div>
</template>
School.vue
- School声明接收
export default{//直接暴露组件的配置对象
name:'School',//需要和文件名保持一致
// 之后school声明接收
props:['getSchoolName'],
data() {
return {
name: '双语幼儿园',
address: '北京'
}
},
getSchoolName函数已经定义完了
- 写按钮做交互
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<!-- 接收完了之后写一个按钮 -->
<button @click="sendSchoolName">把学校名给APP</button>
</div>
</template>
<script>
export default{//直接暴露组件的配置对象
name:'School',//需要和文件名保持一致
// 之后school声明接收
props:['getSchoolName'],
data() {
return {
name: '双语幼儿园',
address: '北京'
}
},
methods: {
sendSchoolName(){
this.getSchoolName(this.name)
}
},
}
</script>
二.自定义事件实现传递数据
通过父组件给子组件绑定一个自定义事件实现:子给父传递数据
//写法一:使用@或者v-on
<Student v-on:xiaomi = "demo"/>由于v-on:在student组件标签上,所以说是给student这个组件的实例对象vc身上绑定了一个事件,事件名字叫xiaomi,如果以后有人触发了这个事件那么demo函数就会被调用
<template>
<div class="app">
<h1>{{msg}}</h1>
<!-- 将这个方法给到school -->
<School :getSchoolName = "getSchoolName"/>
//方式一:
<Student v-on:xiaomi = "demo"/>
</div>
</template>
写demo方法
export default {
name:'App',
components:{School,Student},
data() {
return {
msg:'你好啊'
}
},
// 首先先定义方法
methods: {
getSchoolName(name){
console.log('App收到了学校名',name);
},
demo(){
console.log('demo被调用了');
}
},
}
xiaomi事件是给student组件的实例对象绑定的,想要触发xiaomi事件就需要找student组件的实例对象,所以接下来我们要操作student.vue
因为我们在student组件的实例对象上绑定的xiaomi事件,所以现在触发我们需要找student上的vc
this.$emit('hello',this.name)
this是拿到school身上的vc,$emit的意思是触发
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">把学生名给school组件</button>
</div>
</template>
<script>
export default {
//直接暴露组件的配置对象
name: "Student", //需要和文件名保持一致
data() {
return {
name: '张三',
age: '18'
}
},
methods: {
sendStudentName(){
// 触发student组件实例身上的xiaomi事件
this.$emit('hello',this.name)//拿到vc后触发
}
},
};
//写法二:使用ref
1.在app里面写<Student ref="student"/>
2.然后通过app的实例对象vc(this拿到)this.$refs.student
能获取到student组件的实例对象
3.当app挂载完毕就用this.$refs.student
拿student组件的实例对象,然后就可以绑定事件,调$on
(当…的时候)当‘xiaomi’这个事件被触发的时候执行一个回调getSchoolName,但是这个回调在methods方法中要想拿到需要通过this
mounted(){
this.$refs.student.$on('xiaomi',this.getSchoolName)
}
注:mounted(){}写在哪个组件里面就是哪个组件挂载完毕
- 想要自定义事件触发一次就不能再触发了
mounted(){
this.$refs.student.$once('xiaomi',this.getSchoolName)
}
如果写的是上面两种方法直接在<Student @xiaomi.once = "demo"/>
- 传递的参数很多的话可以采用这种方法,第一个参数作为name形参接收,其他的参数全都整理到params(参数)这个数组上
getSchoolName(name,...params){
console.log('App收到了学校名',name,params);
},
三.两者之间的比较
共同点:必须配置回调
不同点:1.自定义事件的话父组件什么都没给子组件传,只是将事件绑定在子组件身上,将事件方法一直作为回调在使用2.但是另一个却是用props来接收数据
这样看来自定义事件更简单,不用传数据
四.解绑自定义事件
为什么要解绑自定义事件:遵循原则不用就解绑
解绑一个自定义事件:在给哪个组件绑定的事件,就在哪个组件解绑事件,在student.vue的methods方法里定义unbind函数
unbind(){
this.$off('xiaomi')//解绑一个自定义事件
}
解绑多个自定义事件:需要写在数组里
unbind(){
this.$off(['xiaomi','demo'])
}
解绑所有的自定义事件:
unbind(){
this.$off()//解绑所有的自定义事件
}
销毁了当前student组件的实例对象:
death(){
this.$destory()//销毁了当前student组件的实例对象,销毁后所有student实例的自定义事件
}
而在App组件里差值语法里面的来源有三个:1.data(来自于data自己亲自写好)2.props(外部传入)3.computed(计算属性计算出来的)想要用这个name需要把name放在这三个地方
后两个办法都不可用,唯一的办法是将student的子组件交给app的name并且将它放在data里
注意:组件可以用原生事件,想用原生dom事件,要用.native
<Student ref="student" @click.native="show"/>
全局事件总线
全局事件总线可以实现任意组件通信
A组件想收到别人给它的数据 ,在A组件里面就需要写点代码给X绑定自定义事件demo,由于是在A组件里面绑定的自定义事件,所以demo自定义事件的回调就留在了A组件里面。
下面的D组件想给A组件传点数据,在D组件写一段代码去触发x身上的demo自定义事件,并且在触发事件的同时再带点数据666过去,这样的话x身上的demo事件就被触发了,demo事件所对应的回调就会被执行,回调一执行传入的666就以参数的形式到了A组件里面,所以这样就实现了任意组件之间的通信、
其实X就是一个中间商,
步骤:
1.抛出X让所有组件都能看的到,所以我们需要写在main.js里面
main.js为啥要在main.js里写,在哪引入的vue就要在哪操作
/*该文件是整个项目的入口文件*/
//引入vue
import Vue from 'vue'
// 引入APP组件 它是所有组件的父组件
import App from './App.vue'
// 关闭vue的生产提示
Vue.config.productionTip = false
// 创建vue的实例对象 vm
new Vue({
el:'#app',
// 将APP组件放入容器中
render: h => h(App),
//安装全局事件总线
brforeCreate(){
Vue.prototype.$bus = this.name//$bus就是总线x,this指向的是vm
}
})
将x放在vuecomponent原型上放不合适,因为每一次写标签将会生成一个新的vuecomponent,并不好控制。必须将x放在vue的原型对象上这样所有属性和方法都能访问到
$on $off $emit都在vue的原型对象上
student.vue
绑定按钮,点击事件。将事件写在方法里,拿到全局总线触发事件
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">把学生名给school组件</button>
</div>
</template>
<script>
export default {
//直接暴露组件的配置对象
name: "Student", //需要和文件名保持一致
data() {
return {
name: "张三",
age: "18",
}
},
mounted() {},
methods: {
sendStudentName(){
this.$bus.$emit("hello", this.name);
//通过事件总线,触发hello事件,拿到学生名字
}
},
};
</script>
<style lang="less" scoped>
.student {
background-color: pink;
}
</style>
school.vue
通过按钮的点击事件,然后this.$emit
传递事件,然后this.$on
捕获本页面的事件
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
export default {
//直接暴露组件的配置对象
name: "School", //需要和文件名保持一致
data() {
return {
name: "双语幼儿园",
address: "北京",
};
},
mounted() {
// console.log('school',this);
this.$bus.$on("hello", (data) => {
console.log("我是school组件,我收到了数据", data);
//接收hello事件将数据传过来
});
},
};
</script>
<style scoped>
.school {
background: orange;
}
</style>
注意:
在school组件里给傀儡绑定事件,为什么绑定事件是想借助傀儡得到数据,在组件销毁之前最好把傀儡身上的hello事件解绑
只要school组件一挂载就找到总线收数据
mounted() {
// console.log('school',this);
this.$bus.$on("hello", (data) => {
console.log("我是school组件,我收到了数据", data);
});
},
beforeDestroy() {
this.$bus.$off('hello')//拿到总线销毁hello事件
},
};
消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信
需要引入import pubsub from "pubsub-js";
步骤:
1.订阅消息:消息名
2.发布消息:消息内容
C组件里面的东西A组件想用,这样A是需要数据的人,C是数据的提供者,A就相当于我们,C就相当于邮局。
在A组件里面订阅,名叫demo的消息,demo消息带一个text回调函数,如果有人发布了demo消息那么text函数将会被调用。发布的消息名也叫demo,随后携带着数据666。只要C一发布,A这边由于订阅了demo这个消息,text函数就会触发调用,然后666以参数的形式传到text回调里
注意:订阅的时候是什么名字,发布的时候就得是什么名。
需要数据的人订阅消息,提供数据的人发布(发送消息)
原生js里面没有办法实现订阅与发布,我们需要借助第三方库==>pubsub.js
- 先从收数据的人school组件入手,school说我要学生名,快给我
school.vue
需要现在school组件里面引入这段代码
<script>
import pubsub from "pubsub-js";
school组件只要挂载完毕,立刻马上就去订阅一个消息
subscribe是pubsub里面的一个API,第一个参数消息的名字叫hello,第二个参数是消息的回调,msgName是消息名,data是参数
mounted() {
pubsub.subscribe("hello", function (msgName,data) {
console.log("有人发布了hello消息,hello消息的回调执行了",msgName,data);
});
},
- 然后student组件说我已经发布了,你快订阅!
pubsub.publish('hello',666) publish发布hello事件,传递参数666
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">把学生名给school组件</button>
</div>
</template>
<script>
import pubsub from "pubsub-js";
export default {
//直接暴露组件的配置对象
name: "Student", //需要和文件名保持一致
data() {
return {
name: "张三",
age: "18",
}
},
mounted() {},
methods: {
sendStudentName(){
pubsub.publish('hello',666)
}
},
};
</script>
- 每一次订阅的消息都会有一个订阅的ID,当你想取消的时候需要通过ID去取消订阅
mounted() {
this.pubId= pubsub.subscribe("hello", (msgName,data)=> {//改成箭头函数指向正常
console.log("有人发布了hello消息,hello消息的回调执行了",msgName,data);
});
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId)
},