props配置项:
功能:让组件接收外部传过来的数据
传递数据:
接收数据:
① 第一种方式(只接收):props:['name']
② 第二种方式(限制数据类型):props:{name:String}
③ 第三种方式(限制类型、限制必要性、指定默认值)
props:{
name:{
type:String, //类型
required:true, //必要性
default:'划水艺术家' //默认值
}
}
测试案例:
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Student name="划水艺术家" age="20">Student>
div>
template>
src/components/Student.vue
<template>
<div>
<h2>学生姓名:{{name}}h2>
<h2>学生年龄:{{age}}h2>
div>
template>
<script>
export default {
name:'Student',
// 简单声明接收
// props:['name','age']
// 接收的同时对数据进行类型限制
/* props:{
name:String,
age:Number
} */
// 接收的同时对数据进行类型限制 + 指定默认值 + 限制必要性
props: {
name: {
type: String,
required: true,
},
age: {
type: Number,
default: 99
}
}
}
script>
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
子组件无法直接使用父组件中的数据,如果需要使用,则必须由父组件把数据传递给子组件才可以。
本质: 让子组件中的属性与父组件中的属性进行关联绑定, 然后子组件使用该属性, 这样才能做到数据传递。
测试案例:
src/components/Child.vue
<template>
<div>
<h1>子组件引用父组件数据h1>
<h2>姓名:{{name}}h2>
<h2>年龄:{{age}}h2>
div>
template>
<script>
export default {
name: "Child",
props:['name','age']
}
script>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child name="划水艺术家" age="20">Child>
div>
template>
<script>
import Child from "./components/Child";
export default {
name: 'App',
components: {
Child
},
}
script>
子组件无法直接给父组件传递数据,也无法操作父组件中的数据,更无法调用父组件中的方法。
所以,,所谓的子组件向父组件通讯,其实就是想办法让子组件调用父组件的方法,进而响应到父组件中的数据。
测试案例:
src/components/Child.vue
<template>
<div>
<h1>子组件向父组件通信h1>
<h2>姓名:{{name}}h2>
<h2>年龄:{{age}}h2>
<button @click="childFunc">点我年龄 + 1 并将姓名传送至父组件 Appbutton>
div>
template>
<script>
export default {
name: "Child",
// 其中 func 为父组件向子组件传递的方法
props:['name', 'age', 'func'],
methods:{
childFunc(){
// 调用父组件传递的方法
this.func(this.name);
}
}
}
script>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child name="划水艺术家" :age="age" :func="func">Child>
div>
template>
<script>
import Child from "./components/Child";
export default {
name: 'App',
components: {
Child
},
data() {
return {
age: 20
}
},
methods:{
func(name){
this.age += 1
console.log("子组件向父组件通信---name: ", name);
}
}
}
script>
父组件可在子组件上绑定自定义事件,通过触发自定义事件,完成子组件向父组件通信。
组件的自定义事件:
一种组件间通信的方式,适用于子组件向父组件通信
绑定自定义事件:
方式①,在父组件中直接使用 v-on
或 @
绑定自定义事件:
<Child @my-event="func"/>
或
<Child v-on:my-event="func"/>
方式②,在父组件中引用子组件时使用 ref
属性,选取合适时机为子组件绑定事件:
<Child ref="child"/>
<script>
...
mounted(){
this.$refs.child.$on('my-event', func)
}
script>
可在自定义事件上添加事件修饰符
触发自定义事件:this.$emit('my-event', 数据...)
解绑自定义事件:
// 解绑一个自定义事件
// this.$off('my-event')
// 解绑多个自定义事件
// this.$off(['my-event'])
// 解绑所有自定义事件
this.$off()
组件上也可以绑定原生DOM事件,需要使用native
修饰符
注意:通过this.$refs.xxx.$on('my-event', 回调函数)
绑定自定义事件时,回调函数要么配置在methods
中,要么用箭头函数,否则this
指向会出问题!
测试案例:
src/components/Child.vue
<template>
<div>
<h1>子组件向父组件通信h1>
<h2>姓名:{{name}}h2>
<h2>年龄:{{age}}h2>
<button @click="childFunc">点我年龄 + 1 并将姓名传送至父组件 Appbutton>
div>
template>
<script>
export default {
name: "Child",
props:['name', 'age'],
methods:{
childFunc(){
// 触发 Child 组件实例身上的 my-event 事件
this.$emit("my-event", this.name);
}
}
}
script>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child name="划水艺术家" :age="age" v-on:my-event="func">Child>
div>
template>
<script>
import Child from "./components/Child";
export default {
name: 'App',
components: {
Child
},
data() {
return {
age: 20
}
},
methods:{
func(name){
this.age += 1
console.log("子组件向父组件通信---name: ", name);
}
}
}
script>
在上述中绑定的自定义事件是当整个模板一被解析就直接绑定,当我们有些特别的需求,例如:在页面完成挂载5秒后才绑定事件,就需要使用ref
属性。
测试案例:
与②中大致相同,只是绑定自定义事件的方式略有变化
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child name="划水艺术家" :age="age" ref="child">Child>
div>
template>
<script>
import Child from "./components/Child";
export default {
name: 'App',
components: {
Child
},
data() {
return {
age: 20
}
},
methods:{
func(name){
this.age += 1
console.log("子组件向父组件通信---name: ", name);
}
},
// 当页面挂载完毕5秒后给 child 组件绑定事件
mounted(){
setTimeout(()=>{
this.$ref.child.$on("my-event", this.func);
}, 5000);
}
}
script>
给哪个组件绑定的事件,就在哪个组件中完成解绑。
在上述案例中,所定义的自定义事件my-event
是给Child组件绑定的,因此若想要解绑,则在Child组件中完成。
src/components/Child.vue
<template>
<div>
<h1>子组件向父组件通信h1>
<h2>姓名:{{name}}h2>
<h2>年龄:{{age}}h2>
<button @click="childFunc">点我年龄 + 1 并将姓名传送至父组件 Appbutton>
<button @click="unbind">解绑自定义事件button>
div>
template>
<script>
export default {
name: "Child",
props:['name', 'age'],
methods:{
childFunc(){
// 触发 Child 组件实例身上的 my-event 事件
this.$emit("my-event", this.name);
},
unbind(){
// 解绑一个自定义事件
// this.$off('my-event')
// 解绑多个自定义事件
// this.$off(['my-event'])
// 解绑所有自定义事件
this.$off()
}
}
}
script>
全局事件总线(GlobalEventBus):
$on
、$emit
和$off
方法去绑定、触发和解绑事件使用步骤:
new Vue({
...
beforeCreate() {
//安装全局事件总线
Vue.prototype.$bus = this
},
...
})
① 接收数据:A组件想接收数据,则在A组件中给 $bus
绑定自定义事件,事件的回调函数留在A组件自身
export default {
methods(){
// 自定义绑定事件的回调函数
rollbackFunc(data){...}
}
...
mounted() {
// 当页面挂载完成时给当前组件绑定自定义事件
this.$bus.$on('xxx',this.rollbackFunc)
}
}
② 提供数据:this.$bus.$emit('xxx',data)
beforeDestroy
钩子中,用$off
去解绑当前组件所用到的事件测试案例:
案例中将在App.vue
中使用Child1
与Child2
组件,Child1
与Child2
组件互为兄弟组件,以下将展示两者如何进行通信。
src/main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
el:'#app',
render: h => h(App),
beforeCreate() {
//安装全局事件总线
Vue.prototype.$bus = this
}
})
src/components/Child1.vue
<template>
<div>
<h1>兄弟组件通信 Child-1h1>
<h2>Child-1 姓名:{{name}}h2>
<h2>Child-1 年龄:{{age}}h2>
div>
template>
<script>
export default {
name: "Child",
data(){
return{
name: "Child-1",
age: 10
}
},
methods:{
demo(data){
console.log("我是 Child-1 组件,我收到的数据 = ", data);
}
},
mounted() {
// 当页面挂载完毕为 Child1 组件绑定事件
this.$bus.$on("my-event", this.demo)
},
beforeDestroy() {
// 销毁前取消 Child1 组件的绑定事件
this.$bus.$off('my-event')
}
}
script>
src/components/Child2.vue
<template>
<div>
<h1>兄弟组件通信 Child-2h1>
<h2>Child-2 姓名:{{name}}h2>
<h2>Child-2 年龄:{{age}}h2>
<button @click="sendData">点我向Child-1发送数据button>
div>
template>
<script>
export default {
name: "Child2",
data(){
return{
name: "Child-2",
age: 20
}
},
methods:{
sendData(){
// 触发事件
this.$bus.$emit("my-event", this.name);
}
}
}
script>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child1>Child1>
<hr>
<Child2>Child2>
div>
template>
<script>
import Child1 from "./components/Child1";
import Child2 from "./components/Child2";
export default {
name: 'App',
components: {
Child1,
Child2,
}
}
script>
消息订阅与发布(pubsub):
使用步骤:
pubsub-js
npm i pubsub-js
import pubsub from 'pubsub-js'
export default {
methods(){
// 定义回调函数
rollbackFunc(msgName, data){...}
}
...
mounted() {
this.pid = pubsub.subscribe('xxx', this.rollbackFunc);
}
}
pubsub.publish('xxx', data)
pubsub.unsubscribe(pid)
测试案例:
src/components/Child1.vue
<template>
<div>
<h1>兄弟组件通信 Child-1h1>
<h2>Child-1 姓名:{{name}}h2>
<h2>Child-1 年龄:{{age}}h2>
div>
template>
<script>
export default {
name: "Child",
data(){
return{
name: "Child-1",
age: 10
}
},
methods:{
demo(msgName, data){
console.log("我是 Child-1 组件,我收到的数据 = ", data);
console.log("其中 msgName = ", msgName)
}
}
mounted() {
// 当页面挂载完毕 Child1 组件订阅消息
console.log("订阅消息")
this.pid = pubsub.subscribe("my-event", this.demo)
},
beforeDestroy() {
//取消订阅
pubsub.unsubscribe(this.pid )
}
}
script>
src/components/Child2.vue
<template>
<div>
<h1>兄弟组件通信 Child-2h1>
<h2>Child-2 姓名:{{name}}h2>
<h2>Child-2 年龄:{{age}}h2>
<button @click="sendData">点我向Child-1发送数据button>
div>
template>
<script>
export default {
name: "Child2",
data(){
return{
name: "Child-2",
age: 20
}
},
methods:{
sendData(){
// 发布消息
pubsub.publish("my-event", this.name)
}
}
}
script>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Child1>Child1>
<hr>
<Child2>Child2>
div>
template>
<script>
import Child1 from "./components/Child1";
import Child2 from "./components/Child2";
export default {
name: 'App',
components: {
Child1,
Child2,
}
}
script>