前言#
多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点杀鸡用牛刀。Vue 2.4 版本提供了另一种方法:
使用 v-bind=”$attrs”, 将父组件中不被认为 props特性绑定的属性传入子组件中,配合 interitAttrs 选项一起使用。
之所以要提到这两个属性,是因为两者的出现使得组件之间跨组件的通信在不依赖 vuex 和事件总线的情况下变得简洁,业务清晰。
首先分析以下应用场景:
A 组件与 B 组件之间的通信: (父子组件)#
如上图所示,A、B、C三个组件依次嵌套,按照 Vue 的开发习惯,父子组件通信可以通过以下方式实现:
A to B 通过props的方式向子组件传递,
B to A 通过在 B 组件中 $emit
A 组件与 C 组件之间的通信: (跨多级的组件嵌套关系)#
如上图,A 组件与 C 组件之间属于跨多级的组件嵌套关系,以往两者之间如需实现通信,往往通过以下方式实现:
- 借助 B 组件的中转,从上到下props依次传递,从下至上,$emit事件的传递,达到跨级组件通信的效果
- 借助Vuex的全局状态共享
- Vue Bus,使用Vue的实例,实现事件的监听和发布,实现组件之间的传递。
第一种通过props和$emit的方式,使得组件之间的业务逻辑臃肿不堪,B组件在其中仅仅充当的是一个中转站的作用。
第二种 Vuex的方式,某些情况下似乎又有点大材小用的意味,(仅仅是想实现组件之间的一个数据传递,并非数据共享的概念)。
第三种情况的使用在实际的项目操作中发现,如不能实现很好的事件监听与发布的管理,往往容易导致数据流的混乱,在多人协作的项目中,不利于项目的维护。
$attrs
以及$listeners
的出现解决的就是第一种情况的问题,B组件在其中传递props以及事件的过程中,不必在写多余的代码,仅仅是将$attrs
以及$listeners
向上或者向下传递即可。
示例代码
如下所示:
父组件(father.vue)
<template>
<div id="app">
<son
:name="name"
:age="age"
@getName="getName"
@getAge="getAge">
</son>
</div>
</template>
<script>
import son from './son.vue';
export default {
data () {
return {
name:'vicer',
age:18
};
},
components: { son },
methods: {
getName () {
console.log(this.name);
},
getAge () {
console.log(this.age);
}
}
};
</script>
控制台查看结果:
'vicer'
18
子组件(son.vue)
<template>
<div>
<p>我是子组件:</p>
<p>props: {{name}}</p> <!-- 从父组件得到props -->
<p>$attrs: {{$attrs}}</p> <!-- 被跨级传递的属性 -->
<hr>
<!-- 跨级传递事件:在中间组件(B组件)绑定了$listeners属性,孙组件中能直接触发父组件中的getName事件-->
<!-- 跨级传递数据:通过在中间组件绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props-->
<grandson v-bind="$attrs" v-on="$listeners"></grandson>
</div>
</template>
<script>
import grandson from './grandson.vue';
export default {
props: ['name'],
data () {
return {};
},
inheritAttrs: false,
components: { grandson },
mounted () {
this.$emit('getName'); //vicer
}
};
</script>
结果:
我是子组件:
props: vicer
$attrs: { "age": 18}
孙组件 (grandson.vue)
<template>
<div>
<p>我是孙组件:</p>
<p>props: {{age}}</p>
<p>$attrs: {{$attrs}}</p>
<button @click="$listeners.getAge">按钮1</button> <!-- 跨级发送事件方式一 -->
<button @click="sendEvent">按钮2</button> <!-- 跨级发送事件方式二 -->
<hr>
</div>
</template>
<script>
export default {
props: ['age'],
mounted() {
console.log(this.$listeners) //{getName: ƒ, getAge: ƒ}
},
data () {
return {};
},
methods () {
sendEvent(){
this.$emit('getAge');
}
}
};
</script>
结果:
我是孙组件:
props: 18
$attrs: {}
知识点总结#
$attrs
当在父组件向子组件传递数据时,并且子组件不声明props,则$attrs包含所有属性的集合。并且会在子组件的根标签中声明被传递的属性。如:<div name='vicer'></div>
在子组件引入孙组件,并在子组件声明属性v-bind="$attrs"
,则可以跨级传递参数
注意:如果子组件声明了props,则$attrs只包含不被声明的props
$listeners
在子组件引入孙组件,并在子组件中声明v-on="$listeners"
属性,则孙组件可跨级emit事件,让父组件监听到
// 跨级发送事件方式一
<button @click="$listeners.testC">按钮</button>
//跨级发送事件方式二
mounted () {
this.$emit('testC');
}
inheritAttrs
当在父组件向子组件传递数据时,并且子组件不声明props,默认在
但从父级传递过来的属性子组件不可能完全都用到,有一部分可能要传给孙组件,这时就要在子组件中声明inheritAttrs=false
,不继承属性,并通过v-bind=“$attrs"
传递给孙组件
上述特性的使用,完全可以降低在不使用Vuex以及Vue bus的情况下,组件跨级传递复杂度。
—— Here are lovecode3000 ~