vue中组件之间的通信方式
方式
1、父子组件
props / $emit / $parent / ref / $attrs
props:子组件接收父组件中绑定给子组件的值,不包括class、style等保留属性
$emit: 子组件上触发 $emit事件,可将参数作为playload一起传递给父组件
$parent: 子组件获取父实例
ref: 定义在普通元素上获取的是DOM,定义在子组件上获取的是子组件实例
2、兄弟组件
$parent / $root / eventbus / vuex
$root: 访问根组件的属性和方法
3、跨层级组件
eventbus / vuex / provide + inject
$on在vue3中被移除,所以不建议使用eventbus
$children也被移除了: 访问当前组件的所有直接子组件
$listeners也被移除了: 父作用域中所有v-on的事件监听器,但不包含带.native修饰符的
具体实现
// main.js
new Vue({
data() {
return {
rootName: '我是根组件的rootName'
}
},
router,
store,
render: h => h(App)
}).$mount('#app')
爷爷组件
<!-- GrandFather.vue -->
<template>
<div class="grandfa-com">
<!-- tograndFather是孙组件通过$emit触发,在父组件上绑定v-on="$listeners"传递过来的 -->
<FatherCom @tograndFather="grandFatherFn" :toChild="attrToChild"></FatherCom>
</div>
</template>
<script>
import FatherCom from '@/components/FatherCom.vue'
export default {
name: 'GrandFather',
components: {
FatherCom,
},
data() {
return {
grandFather: "我是爷爷",
attrToChild: "通过$attrs向孙组件传值"
}
},
provide() { // 爷爷组件使用依赖注入的方式向孙组件传值
return {
grandFather: this.grandFather
}
},
methods: {
grandFatherFn(val) {
console.log("爷爷接收来自孙子的数据:", val)
}
}
}
</script>
父组件
<!-- FatherCom.vue -->
<template>
<div class="fa-com">
<button @click="getChildren">$children实例</button>
<button @click="getChildRef">$ref</button>
<ChildCom v-bind="$attrs" v-on="$listeners" ref='child' :title="title" :title2="title" :attrTitle="attrTitle" @chToFa="ch2fa" @chToFa2="ch2fa"></ChildCom>
</div>
</template>
<script>
import ChildCom from './ChildCom.vue'
export default {
name: 'FatherCom',
inheritAttrs: false, // 父组件不继承来自爷爷组件透传的值,并在ChildCom绑定v-bind="$attrs"
components: {
ChildCom,
},
data() {
return {
title: "父级传递给子级title",
attrTitle: "通过$attrs获取的属性"
}
},
methods: {
ch2fa(val) {
console.log("c2f:", val)
},
getChildren() {
console.log("children:", this.$children)
},
getChildRef() {
console.log('$ref:', this.$refs.child)
}
}
}
</script>
子组件
<!-- ChildCom.vue -->
<template>
<div class="child-com">
<button @click="toFather">$emit向父级通信</button>
<button @click="toGrandFather">$emit向爷爷级通信</button>
<button @click="getProps">$prop获取父级传递来的数据</button>
<button @click="getListeners">$listeners获取父级绑定的v-on事件</button>
<button @click="getParent">$parent获取父级数据</button>
<button @click="getAttr">$attrs获取透传</button>
<button @click="getRoot">$root获取根元素</button>
<button @click="getInject">获取inject内容</button>
</div>
</template>
<script>
export default {
name: 'ChildCom',
props: ['title', 'title2', 'toChild'], // toChild是爷爷组件绑定的,父组件不继承,直接绑定给子组件的
inject: ['grandFather'],
data() {
return {
cStr: "childComStr",
f2c: this.title
}
},
created() {
console.log("props-title:", this.title)
},
methods: {
toFather() {
this.$emit('chToFa', '子组件传给父组件的消息')
},
toGrandFather() {
this.$emit('tograndFather', '子组件通过v-on="$listeners"向爷爷组件通信')
},
getProps() {
console.log('props:', this.$props)
},
getListeners() {
console.log('listen:', this.$listeners)
},
getAttr() {
console.log('attrs:', this.$attrs)
},
getParent() {
console.log('parent:', this.$parent)
},
getRoot() {
console.log('root:', this.$root.rootName)
},
getInject() {
console.log("inject:", this.grandFather)
}
}
}
</script>
EventBus事件总线的实现
首先先初始化一个EventBus,全局事件总线,2种方式
方式一:创建一个eventbus.js,然后需要用到的页面import,也可以在main.js中全局引入
// eventbus.js
import Vue from 'vue'
export const EventBus = new Vue();
// main.js
import { EventBus } from './js/eventbus'
Vue.prototype.$EventBus = EventBus
方式二:直接在main.js里定义
// main.js
Vue.prototype.$EventBus = new Vue()
采用mian.js中直接定义的方式完成下面的实现:
A.vue:
<!-- A.vue -->
<template>
<button @click="sendMsg">发送MsgA</button>
</template>
<script>
export default {
name: 'AP',
data(){
return{
MsgA: 'A组件中的Msg'
}
},
methods: {
sendMsg() {
this.$router.push('/b')
}
},
destroyed() {
this.$EventBus.$emit("aMsg", this.MsgA);
}
};
</script>
B.vue:
<!-- B.vue -->
<template>
<p>{{msgB}}</p>
</template>
<script>
export default {
name: 'BP',
data(){
return {
//初始化一个msgB
msgB: ''
}
},
created() {
this.$EventBus.$on("aMsg", (data) => {
this.msgB = data;
this.$EventBus.$off('aMsg'); // 移除事件总线,避免频繁触发和内存泄漏
});
}
};
</script>
这样的话从A页面跳转到B页面时就会把数据通过eventbus传递过来;
要想B页面DOM内容同步更新要注意两个事项:
1、A页面应该把EventBus放在destoryed中触发,放事件中触发的话数据在B页面中可以拿到,但是页面不会更新
2、B页面要在ceated中接收EventBus,否则B页面可以拿到数据但也不会更新
这是因为EventBus的$on必须在 $emit 之前生成,否则会出现无法监听数据的bug,当A页面跳转去B页面的时候会先执行B的created,再执行A的destoryed,最后执行B的mounted,显然,只有在A的destoryed中emit在B的created中on才会监听到数据更新页面
当然,vue3已经移除$on方法,所以不建议使用EventBus了