在之前的文章中我們提到了vue常用的幾種通信方式,如父子,子父,以及兄弟組件之間的通信,可以通過這個傳送門了解他們:Vue通信方式(一)
當我們如果遇到祖組件,父組件,孫組件,三個級別嵌套時,我們該怎么在祖組件與孫組件之間通信呢,當然通過本地存儲或者Vuex都可以實現,但僅僅是一個值得時候未免有點小題大做了,或者是組件之間的通信那樣一級一級傳?有點麻煩,在此,我們就詳解如何實現祖孫之間的通信。
首先我們來看看vue新增的兩個屬性:$attrs和$listeners,這是vue2.4新增的兩個屬性,我們來看看官方文檔是怎么解釋的吧。
好吧,對於第一次看的童鞋來說,並不知道他在BB什么, 簡要的講就是,$attrs可以獲取父作用域傳入的值(不包括props中的),$listeners相當於父作用域的事件監聽器,那我們就可以用這兩個屬性實現祖孫之間的數據通信
首先,我們定義一個祖級別的組件:father1.vue
<template> <h6>父組件: <p>這是來自c組件的數據:{{msg1}}</p> <div> <B :messagec="messagec" :msgc="msgc2" v-on:getCData="getCData"></B> </div> </h6> </template> <script> import B from './son.vue' export default{ data() { return { messagec:{a:1,c:2}, //傳遞給c組件的數據,可以傳對象,字符串以及其他類型 msg:'', msg1:'', msgc2:'第二個傳給孫組件的值' } }, components:{ B }, methods:{ //執行C子組件觸發的事件 getCData(val){ console.log("這是來自C組件的數據:"+val) this.msg1=val } } } </script>
然后在定義一個父級別的組件:son.vue,相當於祖孫通信之間的中間件
<template> <h1>這是子組件: <div> <p>這是B組件</p> <!-- C組件中能直接觸發getCData的原因在於 B組件調用C組件時 使用 v-on 綁定了$listeners 屬性 --> <!-- 通過v-bind 綁定$attrs屬性,C組件可以直接獲取到A組件中傳遞下來的props(除了B組件中props聲明的) --> <C v-bind="$attrs" v-on="$listeners"></C> </div> </h1> </template> <script> import C from './c.vue' // 引入C組件 export default{ data(){ return { mymessage:'' } }, components:{ C }, props:{ // messagec:String // 當在這個組件聲明了傳向C組件的值時,C組件則通過$attrs.messagec得不到該值,相當於被攔截了,要傳給C,則不能在此聲明 }, methods:{ } } </script>
主要是綁定兩個屬性v-bind="$attrs" v-on="$listeners",並且不能在props中聲明傳入的值,"$attrs"用於接受father.vue傳入的值,$listeners用於監聽觸發事件傳值給father.vue.
最后我們定義一個孫級別的組件:c.vue
<template> <h1>這是C組件: <p>接受來自father的值:{{$attrs.messagec.a}}</p> <p>接受來自father第二個的值:{{$attrs.msgc}}</p> <input type="text" v-model="msgtofather" @input="passCData(msgtofather)"> </h1> </template> <script> export default{ data(){ return{ msgtofather:'' //傳給father組件的值 } }, props:{ // messagec:String // 當在這個組件聲明了傳向C組件的值時,C組件則通過$attrs.messagec得不到該值,相當於被攔截了,要傳給C,則不能在此聲明 }, methods:{ passCData(val){ //觸發父組件father中的事件 this.$emit('getCData',val) } } } </script>
注意:此組件中props也不能聲明父組件傳入的值,否則$attrs.messagec.a會取不到該值,並且可以接受多個來自祖組件的值
最后,組件通信的測試圖如下: