一般来说,组件可以有以下几种关系:
如上图所示,A 和 B、B 和 C/D 都是父子关系,C 和 D 是兄弟关系,A 和 C/D 是隔代关系。
vue组件之间的通信6种
- 父子通信:props/$emit
- 兄弟通信:eventHub($emit / $on)
- vue 的状态管理器,存储的数据是响应式:vuex
- 多级组件嵌套:$attrs / $listeners / inheritAttrs
- 注入依赖:provide/inject
- 组件访问:$parent / $children与 ref
$attrs / $listeners/ inheritAttrs说明
假如我们想让A和C进行通信,有些小伙伴就会说,我们可以借助 B 组件的中转,从上到下props依次传递,从下至上emit 事件的传递,达到跨级组件通信的效果。
很显然,通过props/$emit的方式使得组件之间的业务逻辑臃肿不堪。
为了解决这个问题,vue2.4的时候新增了 $attrs / $listeners / inheritAttrs,详情查看官网。
$attrs:
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=“$attrs” 传入内部组件,通常配合 interitAttrs 选项一起使用。
$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件。
inheritAttrs
:默认为true, 会自动在挂载组件元素上属性值,如props声明了属性,则挂载未被声明的属性;false时会关闭自动挂载到组件根元素上属性。注意:这个选项不影响 class 和 style 绑定。
备注一下
:在Vue3.0版本中,$listeners / inheritAttrs已被移除掉,现在事件监听器是 $attrs 的一部分。
这里不作详细说明。请移步到我的另外一篇文章:Vue3.0跨级/嵌套组件通信attrs
案例组件(父子孙)
其实官网概念说明有点别扭,我们还是先看案例吧。
我们分别创建3个组件:父组件:parent.vue,子组件:child.vue,孙组件:grandchild.vue
- 父组件:parent.vue
<template>
<div class="parent-root">
<child
class="child"
:name="name"
:age="age"
@change="change"
>
</child>
<p>孙组件传出来的值:{{text}}</p>
</div>
</template>
<script>
import child from "@/views/test/child";
export default {
name: 'parent',
components: {child},
props: {},
data() {
return {
name:"小明",
age:18,
text:'',
};
},
computed: {},
created() {
},
methods: {
//孙组件传出来的事件
change(text) {
this.text = text;
}
},
};
</script>
<style scoped lang="scss">
.parent-root {
padding: 20px;
font-size: 16px;
/deep/ p{ margin-bottom: 10px;}
}
</style>
- 子组件:child.vue
<template>
<div class="child-root">
<grandchild
v-bind="$attrs"
v-on="$listeners"
>
</grandchild>
</div>
</template>
<script>
import grandchild from "@/views/test/grandchild";
export default {
name: 'child',
components: {grandchild},
props: [],
//inheritAttrs:
//默认为true, 会自动在挂载组件元素上属性值,如props声明了属性,则挂载未被声明的属性
//为false时会关闭自动挂载到组件根元素上属性
inheritAttrs: false,
data() {
return {};
},
computed: {},
created() {
console.log('$attrs:',this.$attrs); //$attrs: { "name": "小明", "age": 18 },这里不会输出"class":"child"
},
methods: {},
};
</script>
<style scoped lang="scss">
.child-root {}
</style>
- 孙组件:grandchild.vue
<template>
<div class="grandchild-root">
<p>name:{{name}}</p>
<p>age:{{age}}</p>
<button @click="bindChange">孙子点击传给爷爷</button>
</div>
</template>
<script>
export default {
name: 'grandchild',
components: {},
props: {
name: {
type: String,
default: ''
},
age:{
type: Number,
default: null
},
},
data() {
return {};
},
computed: {},
created() {
},
methods: {
bindChange() {
let text = '爷爷,您好'
this.$emit('change',text);
}
},
};
</script>
<style scoped lang="scss">
.grandchild-root {}
</style>
层级关系截图
另外说一下:inheritAttrs
假如:我们在子组件child.vue设置inheritAttrs
- 当inheritAttrs: true
-
当inheritAttrs: true, props:[‘name’]
-
当inheritAttrs: false
输出结果
如上图所示$attrs
表示没有继承数据的对象,格式为{属性名:属性值}。$listeners 来传递数据与事件,跨级组件之间的通讯变得更为便捷。
小结
简单来说:$attrs
和 $listeners
是两个对象,$attrs
里存放的是父组件中绑定的非 props 属性,$listeners
里存放的是父组件中绑定的非原生事件。inheritAttrs
选项则为是否挂载到组件元素的attribute。