组件交互/通信/传值
组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
1.子组件内使用父组件数据 父-子
1.父组件传递数据给子组件 (可以传递静态属性 动态属性 )
<my-com title='' :msg='msg' attr-a=''>
2.子组件接收并处理数据
{
props:['title','msg','attrA],
template:``
}
App.vue
<template>
<div id="app">
<Shcool />
<Student name="科大" :age="19" />
</div>
</template>
<script>
import Shcool from "./components/Shcool.vue";
import Student from "./components/Student.vue";
export default {
name: "app",
components: { Shcool, Student },
};
</script>
<style>
#app {
background-color: orange;
padding: 15px;
}
</style>
Student.vue
<template>
<div id="student">
<h1>学生姓名:{{ name }}</h1>
<h1>学生年龄:{{ age + 1 }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
// studentName: "好学生",
// age: "18",
};
},
props: ["name", "age"],
};
</script>
<style>
#student {
background-color: skyblue;
}
</style>
2.父组件内使用子组件的数据
事件发射 自定义事件
在子组件内发射 在父组件内接受
子组件发射时机
1.手动按钮发送
2.监听子组件内数据变化,然后发射
2.1 props实现子传父
props传参同props父传子参数类似,具体实现原理是父组件定义函数,并将函数传递给子组件,在子组件中调用,并将自己的数据作为参数传递给父组件的函数。大体可分为四步。
第一步,在子组件 标签上 上使用v-bind(简写为 :)绑定传递的参数名以及传递的处理函数;
第二步,在子组件的 script 中 添加 props 属性 ,并将 参数名 接收;
第三步,在子组件中调用其他函数来触发用 props接收的参数(也就是那个函数)
第四步,子组件的参数被传递出去
App.vue
<template>
<div id="app">
<Shcool :getSchoolName="getSchoolName" />
<!-- <Student name="科大" :age="19" /> -->
<Student />
</div>
</template>
<script>
import Shcool from "./components/Shcool.vue";
import Student from "./components/Student.vue";
export default {
name: "app",
components: { Shcool, Student },
methods: {
getSchoolName(name) {
console.log("App收到了学校名:", name);
},
},
};
</script>
<style>
#app {
background-color: orange;
padding: 15px;
}
</style>
School.vue
<template>
<div id="school">
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ addddress }}</h1>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
//在子组件的 script 中 添加 props 属性 ,并将 参数名 接收;
props: ["getSchoolName"],
data() {
return {
name: "科大",
addddress: "上海",
};
},
methods: {
sendSchoolName() {
this.getSchoolName(this.name);
},
},
};
</script>
<style>
#school {
background-color: skyblue;
}
</style>
2.2 自定义事件实现子向父传递参数
2.2.1 第一种自定义事件
靠函数参数传递的方法将数据带给父组件,具体分三步:
第一步,在子组件 标签上 上使用v-on(简写为 @)绑定自定义的 事件名 以及对应的处理函数,可以类比原生事件 @click=“handle(e)”;
第二步,在子组件中调用其他函数,使用 this.$emit(‘事件名’,参数) 的方法触发事件;
第三步,子组件的参数被传递出去
App.vue
<template>
<div id="app">
<Shcool :getSchoolName="getSchoolName" />
<!-- <Student name="科大" :age="19" /> -->
<Student v-on:lili="getStudentName" />
</div>
</template>
<script>
import Shcool from "./components/Shcool.vue";
import Student from "./components/Student.vue";
export default {
name: "app",
components: { Shcool, Student },
methods: {
getSchoolName(name) {
console.log("App收到了学校名:", name);
},
getStudentName(name) {
console.log("App收到学生名:", name);
},
},
};
</script>
<style>
#app {
background-color: orange;
padding: 15px;
}
</style>
Student.vue
<template>
<div id="student">
<h1>学生姓名:{{ name }}</h1>
<h1>学生年龄:{{ age }}</h1>
<button @click="sendStudentName">把学生名给App</button>
</div>
</template>
<script>
export default {
data() {
return {
name: "好学生",
age: "18",
};
},
// props: ["name", "age"],
methods: {
sendStudentName() {
this.$emit("lili", this.name);
},
},
};
</script>
<style>
#student {
background-color: skyblue;
}
</style>
2.2.2 第二种自定义事件
Student组件不需要任何变化
主要变动有两处,第一处 是子组件标签中 使用ref属性;第二处 是不在methods或者watch中 定义处理函数,而是在生命周期函数中处理。
这样的好处是更加灵活,自定义事件可以添加其他异步操作,且对子组件绑定多个自定义事件时,也无需考虑太多的逻辑,不断地在 生命周期函数中 使用 $on 来处理即可。
$on 接收两个参数,$on(自定义事件名,函数) 而函数可以用箭头函数来简写操作
注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据
-->
<School :getSchoolName="getSchoolName" />
<hr />
<!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第一种写法,使用@或v-on) -->
<Student @lili="getStudentName" />
<!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第二种写法,使用ref) -->
<Student ref="student" />
</div>
</template>
<script>
import Student from "./components/Student.vue";
import School from "./components/School.vue";
export default {
name: "App",
components: { Student, School },
data() {
return {
msg: "你好啊",
};
},
methods: {
getSchoolName(name) {
console.log("App收到了学校名:", name);
},
getStudentName(name) {
console.log("App收到了学生名:", name);
},
},
mounted() {
this.$refs.student.$on("lili", this.getStudentName);
},
};
</script>
<style scoped>
.app {
background-color: gold;
padding: 10px;
}
</style>
3 全局事件总线(GlobalEventBus)
全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:
- 所有的组件对象都必须能看见他
- 这个对象必须能够使用
$on
、$emit
和$off
方法去绑定、触发和解绑事件
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:'#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线
}
})
App.vue
<template>
<div class="app">
<School />
<Student />
</div>
</template>
<script>
import Student from "./components/Student.vue";
import School from "./components/School.vue";
export default {
name: "App",
components: { Student, School },
data() {
return {};
},
};
</script>
<style scoped>
.app {
background-color: rgb(241, 207, 11);
padding: 10px;
}
</style>
Student.vue
<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: "好学生",
sex: "男",
};
},
methods: {
sendStudentName() {
this.$bus.$emit("demo", this.name);
},
},
};
</script>
<style scoped>
.student {
background-color: red;
}
</style>
School.vue
<template>
<div class="dome">
<h2>学校姓名:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
name: "科大",
address: "北京",
};
},
methods: {
demo(data) {
console.log("我是School组件,收到了数据:", data);
},
},
mounted() {
this.$bus.$on("demo", this.demo);
},
beforeDestroy() {
this.$bus.$off("demo");
},
};
</script>
<style >
.dome {
background-color: skyblue;
}
</style>
总结:
全局事件总线(GlobalEventBus):
- 一种组件间通信的方式,适用于任意组件间通信
- 安装全局事件总线:
new Vue({
...
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
...
})
使用事件总线:
- 接收数据:A组件想接收数据,则在A组件中给
$bus
绑定自定义事件,事件的回调留在A组件自身
export default {
methods(){
demo(data){...}
}
...
mounted() {
this.$bus.$on('xxx',this.demo)
}
}
2.提供数据:`this.$bus.$emit('xxx',data)`
最好在beforeDestroy
钩子中,用$off
去解绑当前组件所用到的事件
4 消息的订阅预发布
School.vue
<template>
<div class="dome">
<h2>学校姓名:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
import pubsub from "pubsub-js";
export default {
name: "School",
data() {
return {
name: "科大",
address: "北京",
};
},
methods: {
demo(msgName, data) {
console.log("我是School组件,收到了数据:", data);
},
},
mounted() {
this.pubId = pubsub.subscribe("demo", this.demo); //订阅消息
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId); //取消订阅
},
};
</script>
<style >
.dome {
background-color: skyblue;
}
</style>
Student.vue
<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: "好学生",
sex: "男",
};
},
methods: {
sendStudentName() {
// this.$bus.$emit("demo", this.name);
pubsub.publish("demo", this.name); //发布消息
},
},
};
</script>
<style scoped>
.student {
background-color: red;
}
</style>
总结:
消息订阅与发布(pubsub):
消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信
使用步骤:
安装pubsub:npm i pubsub-js
引入:import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
export default {
methods(){
demo(data){...}
}
...
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo)
}
}
提供数据:pubsub.publish('xxx',data)
最好在beforeDestroy钩子中,使用pubsub.unsubscribe(pid)取消订阅