组件通信的方式
- props
- $emit
- $listener 和 $attrs
- 全局事件总线
- vuex
props
父组件
<template>
<div>
First
<second :fatherMessage="fatherMessage" />
</div>
</template>
<script>
import second from './Second'
export default {
name: 'First',
data(){
return {
fatherMessage:'come from first'
}
},
components: {
second
},
}
</script>
子组件
<template>
<div>
{{fatherMessage}}
</div>
</template>
<script>
export default {
name: 'Second',
props: {
fatherMessage:{
type: String,
default: ''
}
},
data(){
return {}
}
}
</script>
效果
其中props可以自定义验证方式:
-
type:用来指值的类型,有以下值可选
String
Number
Boolean
Array
Object
Date
Function
Symbol
当有多个可能的类型时,可以通过propA: [String, Number]
方式传递 -
required:是否必传
-
default:默认值
对于对象或数组的默认值需要通过一个工厂函数获取propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } },
允许自定义函数进行验证
propE: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } }
prop存在单向数据流这种规定,即:子组件不能修改父组件传入的值。
$emit
子组件
<template>
<div>
<button @click="sendMsgToFirst">向First发送消息</button>
</div>
</template>
<script>
export default {
name: 'Second',
data(){
return {}
},
methods: {
sendMsgToFirst(){
this.$emit('getMsgFromSecond','msg from second')
}
}
}
</script>
父组件
<template>
<div>
First
<second v-on:getMsgFromSecond="getMsgFromSecond"/>
</div>
</template>
<script>
import second from './Second'
export default {
name: 'First',
data(){
return {
fatherMessage:'come from first'
}
},
components: {
second
},
methods: {
getMsgFromSecond(val){
console.log(val);
}
}
}
</script>
效果
该方法用于vue实例,仅限于子组件向父组件传值
$listener 和 $attrs
孙子(及以下)组件
<template>
<div>
adasd
</div>
</template>
<script>
export default {
name: 'Third',
data(){
return {}
},
components: {
},
mounted(){
console.log('this.$attrs',this.$attrs);
console.log('this.$listeners',this.$listeners);
this.$listeners.eventFromFirst()
}
}
</script>
子组件
<template>
<div>
second
<third v-bind="$attrs" v-on="$listeners"/>
</div>
</template>
<script>
import third from './Third'
export default {
name: 'Second',
data(){
return {}
},
components: {
third
},
methods: {
}
}
</script>
父组件
<template>
<div>
First
<second :msgFromFirst="msgFromFirst" @eventFromFirst="eventFromFirst"/>
</div>
</template>
<script>
import second from './Second'
export default {
name: 'First',
data(){
return {
msgFromFirst:'come from first'
}
},
components: {
second
},
methods: {
eventFromFirst(){
console.log('a event from first');
}
}
}
</script>
效果
此种方式较prop更适合多及组件的通信。同时,只要我们在子组件接收某个属性,那么该属性就会被子组件“截胡”,孙子组件们就无法在拿到了。
用官方的话来说,即:
$attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的attribute
绑定 (class
和style
除外)。当一个组件没有声明任何prop
时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
对于$listeners
来说
包含了父作用域中的 (不含
.native
修饰器的)v-on
事件监听器。它可以通过v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
全局事件总线
main.js
new Vue({
......
beforeCreate(){
Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vue
}
......
})
组件A
<template>
<div>
First
<second/>
</div>
</template>
<script>
import second from './Second'
export default {
name: 'First',
data(){
return {
}
},
components: {
second
},
methods: {
},
mounted(){
this.$bus.$emit('globalEvent','a msg from first')
}
}
</script>
组件B(主要表示C和A不是父子关系)
<template>
<div>
second
<third/>
</div>
</template>
<script>
import third from './Third'
export default {
name: 'Second',
data(){
return {}
},
components: {
third
},
methods: {
}
}
</script>
组件C
<template>
<div>
third
</div>
</template>
<script>
export default {
name: 'Third',
data(){
return {}
},
components: {
},
mounted(){
this.$bus.$on('globalEvent',(val)=>{
console.log('this is third >>>',val);
})
}
}
</script>
效果
该种方法可以做到任意组件的通信,但是要注意在组件创建后注册事件,在组件销毁后通过$off解绑
Vuex
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
详见文档:https://vuex.vuejs.org/zh/