转载:详细讲解vue中祖孙组件间的通信之使用$attrs和$listeners的方式 - 知乎
项目结构图
$attrs
$attrs是一个容器对象,用来存放该组件的父组件传来的,且未使用props进行声明获取的数据
爷组件:
<template>
<div id="app">
我是爷组件
<fu
:msg1="msg1"
:msg2="msg2"
:msg3="msg3"
:msg4="msg4"
></fu>
</div>
</template>
<script>
import fu from "./views/fu.vue";
export default {
components: {
fu,
},
data() {
return {
msg1: "孙悟空",
msg2: "猪八戒",
msg3: ["白骨精", "玉兔精", "狐狸精"],
msg4: {
name: "炎帝萧炎",
book: "斗破苍穹",
},
};
},
};
</script>
<style lang="less" scoped>
#app {
width: 950px;
height: 600px;
box-sizing: border-box;
border: 3px dashed #e9e9e9;
background-color: #cde;
margin: 50px;
}
</style>
此时爷组件通过:(v-bind)向父组件中传递了四个数据,此时在父组件中的$attrs会存放爷组件传递过去的,且未进行props声明的数据
<template>
<div class="fatherClass">
我是父组件
<h2>{{ msg1 }}</h2>
<h2>{{ $attrs.msg2}}</h2>
<h2>{{ $attrs.msg3}}</h2>
<h2>{{ $attrs.msg4}}</h2>
</div>
</template>
<script>
export default {
name: "DemoFather",
props: {
msg1: {
type: String,
default: "",
},
},
mounted() {
console.log('fu组件实例',this);
},
};
</script>
<style lang="less" scoped>
.fatherClass {
width: 850px;
height: 400px;
background-color: #baf;
margin-left: 50px;
margin-top: 50px;
}
</style>
此时在父组件中再通过v-bind="$attrs",将爷组件传来的数据再传送给孙组件,即可实现爷孙组件的通信,在孙组件中正常接收。
<template>
<div class="sunClass">
我是孙子组件
<h2>接收爷组件数据:-->{{ msg2 }}</h2>
<h2>接收爷组件数据:-->{{ msg3 }}</h2>
<h2>接收爷组件数据:-->{{ msg4 }}</h2>
</div>
</template>
<script>
export default {
// $attrs一般搭配interitAttrs 一块使用
inheritAttrs: false, // 默认会继承在html标签上传递过来的数据,类似href属性的继承
/*
孙子组件通过props,就能接收到父组件传递过来的$attrs了,就能拿到里面的数据了,也就是:
爷传父、父传子。即:祖孙之间的数据传递。
*/
props: {
msg2: {
type: String,
default: "",
},
msg3: {
type: Array,
default: () => {
return [];
},
},
msg4: {
type: Object,
default: () => {
return {};
},
},
},
name: "DemoSun",
};
</script>
<style lang="less" scoped>
.sunClass {
width: 750px;
height: 180px;
background-color: #bfa;
margin-top: 80px;
margin-left: 50px;
}
</style>
$listeners
inheritAttrs默认值true,继承父组件除props之外的所有属性;为false只继承class属性。
通过父组件也可将孙组件的数据传递到爷组件中。$listeners将孙组件的emit方法通知到爷组件中。
1.在父组件中给孙组件的调用加上
<sun v-bind="$attrs" v-on="$listeners"></sun>
2.在爷组件中定义事件,并将事件传给父组件
<template>
<div id="app">
我是爷组件
<h3>{{ fromSunData }}</h3>
<fu :msg1="msg1" :msg2="msg2" :msg3="msg3" :msg4="msg4"
@fromSun="fromSun">
</fu>
</div>
</template>
<script>
import fu from "./views/fu.vue";
export default {
components: {
fu,
},
data() {
return {
msg1: "孙悟空",
msg2: "猪八戒",
msg3: ["白骨精", "玉兔精", "狐狸精"],
msg4: {
name: "炎帝萧炎",
book: "斗破苍穹",
},
fromSunData: "",
};
},
methods: {
fromSun(payload) {
console.log("孙传祖", payload);
this.fromSunData = payload;
},
},
};
</script>
3.孙组件触发爷组件的事件
<template>
<div class="sunClass">
我是孙子组件
<h2>接收爷组件:-->{{ msg2 }}</h2>
<h2>接收爷组件:-->{{ msg3 }}</h2>
<h2>接收爷组件:-->{{ msg4 }}</h2>
<el-button size="small" type="primary" plain @click="sendToZu">孙传祖</el-button>
</div>
</template>
<script>
export default {
// $attrs一般搭配interitAttrs 一块使用
inheritAttrs: false, // 默认会继承在html标签上传递过来的数据,类似href属性的继承
props: {
msg2: {
type: String,
default: "",
},
msg3: {
type: Array,
default: () => {
return [];
},
},
msg4: {
type: Object,
default: () => {
return {};
},
},
},
name: "DemoSun",
data() {
return {
data: "来自孙组件的数据",
};
},
methods: {
sendToZu() {
// 孙组件能够触发爷组件的fromSun方法的原因还是因为父组件中有一个$listeners作为中间人,去转发这个事件的触发
this.$emit("fromSun", this.data);
},
},
};
</script>