vue中组件是模块化开发的基础,在组件A中使用了组件B,那他们必然需要数据通信,父组件要将数据传递给自组建,子组件要通知父组件自己内部发生的事情。
在vue.js中父子组件的关系可以总结为props down, events up,父组件通过props向下传递数据给子组件,子组件通过events给父组件反馈消息,引用官方的一张图,来看看他们是怎么工作的
1、父子组件通信
①父组件向子组件传递数据,子组件需要显式的用props选项声明它期待接受的数据
父组件传递数据:
<parent>
<child :msg="msg"></child>
</parent>
export default {
data() {
return {
msg: [1, 2, 3]
}
}
}
子组件接收数据:
export default {
props: {
msg: {
type: Array,
default() {
return [] // 这里可以写默认值
}
}
}
}
初学者常犯的错误是使用字面量语法传递数值
<child some-props="1"></child>
它是一个字面prop,传递的是字符串1,而不是number,如果想传递number类型的数值,需要使用v-bind。把它的值当作JaveScript表达式计算:
<child v-bind:some-props="1"></child>
在vue中,子组件是不允许直接修改父组件通过props传递过来的数据,这样会导致父组件和子组件耦合,并且父组件的数据可能会被任意子组件修改,另外每次父组件更新,子组件相应的props都会更新为最新值。之所以想修改props中的数据是因为:
1、我们希望把props中的数据当作局部数据使用
2、希望把props中的数据处理成其他数据使用
对于这两种方式我们应该正确的处理为:
定义一个局部变量,并用props的值初始化它
props: {
someProp: {
type: String,
default: ""
}
},
data () {
return {
initData: this.someProp
}
}
定义一个computed,处理props的值并返回
props: {
someProp: {
type: String,
default: ""
}
},
computed: {
normalData() {
return this.someProp.trim().toLowerCase()
}
}
需要注意的是:JavaScript中数组和对象是引用类型,指向同一内存空间,如果props是数组或对象,在子组件内部直接改变它会影响父组件的状态。
②子组件与父组件通信
我们知道,父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!
父组件可以直接在子组件使用的地方使用v-on绑定事件
父组件绑定事件和传递数据:
<parent>
<child @upup="change" :msg="msg"></child> //监听子组件触发的upup事件,然后调用change方法
</parent>
export default {
data() {
return {
msg: '123'
}
},
methods: {
change(msg) {
this.msg = msg; // 接收子组件传递过来的参数,并更新msg,从而子组件也相应的更新msg
}
}
}
子组件接收数据和触发事件:
<template>
<div @click="up">{{msg}}</div>
</template>
export default {
props: {
msg: {
type: String,
default: ''
}
},
methods: {
up() {
this.$emit('upup','updateMsg'); //主动触发upup方法,'updateMsg'为向父组件传递的数据
}
}
}
③ 父组件如何触发子组件的事件
<parent @click="doSomeThing">
<child ref="childComp"></child>
</parent>
export default {
methods: {
doSomeThing() {
this.$refs.childComp.yesSir('123')
}
}
}
<template></template>
export default{
methods: {
yesSir(a) {
console.log(a) // 123
}
}
}
2、非父子组件通信
在简单的场景下,vue建议我们使用一个空的 Vue 实例作为中央事件总线:
新建一个bus.js用于存储我们空的事件中转站:
import Vue from 'vue'
export var bus = new Vue()
组件a通过$on绑定事件:
<template>
</template>
import { bus } from 'bus.js'
export default {
created() {
bus.$on('change', () => { //接收事件
this.msg = 'hehe';
});
}
}
组件b通过$emit绑定事件:
<div @click="eve"></div>
import { bus } from 'bus.js'
export default {
methods: {
eve() {
bus.$emit('change','hehe'); //触发事件
}
}
}