前言#
多級組件嵌套需要傳遞數據時,通常使用的方法是通過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的情況下,組件跨級傳遞復雜度。