Vue祖孫組件怎么傳值
先看基礎
祖孫組件,也就是 3 層嵌套的組件。關於 vue 中父子組件之間的數據傳遞是通過 props 和 $emit 實現,參考Vue 父子組件傳值。
那祖孫組件之間傳值怎么實現,先了解下面的幾個 vue 屬性。
$props
當前組件接收到的 props 對象。Vue 實例代理了對其 props 對象 property 的訪問。
$attrs
$attrs 是一個 Object,它包含了父作用域中不作為 prop 被識別 (且獲取) 的 attribute 綁定 (class 和 style 除外)。
如果組件沒有聲明任何 prop 時,這里會包含所有父作用域的綁定 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部組件。
怎么理解呢?
就是父組件綁定到子組件上的屬性,在子組件中沒有聲明 props 進行接收的那些屬性會被包含在 attrs 中,舉個栗子🌰
🎈 在父組件中對子組件綁定了兩個屬性 data1 data2。
parent.vue
<Child :data1="data1" :data2="data2" />
<script>
import Child from './child'
...
data(){
return{
data1: 'String',
data2: ['String','Array']
}
}
...
</script>
🎈 但在子組件的props只對 data1 做了聲明接收,那 data2 就會被包含在 $attrs 中。在子組件中也是可以取到 $attrs 的值的,既然是對象,那就還可以按照屬性名來取的。
child.vue
<template>
<div>
<div>$attrs: {{$attrs}}</div>
<div>data2: {{$attrs['data2']}}</div>
</div>
</template>
<script>
...
props: ['data1'], /* <--看這里,只對data1做聲明接收 */
data(){
return{...}
}
...
</script>
😀 其實到這里還沒有結束,接着聊聊 inheritAttrs 吧。不過這和傳值之間關系不大。
inheritAttrs 是 vue 2.4.0 的新增選項,官方的介紹是醬紫的。
1️⃣ 默認情況下父作用域的不被認作 props 的 attribute 綁定 (attribute bindings) 將會“回退”且作為普通的 HTML attribute 應用在子組件的根元素上。當撰寫包裹一個目標元素或另一個組件的組件時,這可能不會總是符合預期行為。通過設置 inheritAttrs 到 false,這些默認行為將會被去掉。
2️⃣ 而通過 (同樣是 2.4 新增的) 實例 property $attrs 可以讓這些 attribute 生效,且可以通過 v-bind 顯性的綁定到非根元素上。
🚫 注意:這個選項不影響 class 和 style 綁定。
第1️⃣簡單理解就是前面說的,在子組件中的 props 沒有聲明接收的屬性(也就是 $attrs 所包含的屬性)會被綁定到這個子組件的 HTML 根節點上,我們檢查代碼也是可以看到的。
就像下面的例子,來自父組件的消息沒有被接收時會作為屬性被渲染到子組件的根節點上。
然后是使用 inheritAttrs: false 可以避免被渲染。
第2️⃣說的就是可以通過 v-bind="$attrs" 把這些屬性綁定到其他的節點上(包括子節點,這是祖孫組件傳值的技術基礎)
$listeners
vue 2.4.0 新增
$ listeners 是個 Object。包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部組件。
祖傳孫
在 vue 中,祖孫組件之間是不能直接通信的,需要通過父組件作為 中間組件。實際上祖父的關系就是兩個 父與子 的關系。
1 $props 鏈
【不推薦使用】
既然是兩個父與子之間的關系,那就可以 祖傳父 再由 父傳子。而 props 可以用來接收來自父組件的值,那就可以通過 props 實現鏈式傳遞了。不舉栗子🌰了,舉個香蕉吧🍌。
傳遞次序:GrandFather → Father → GrandSon
① 在 GrandFather 中給 Father 傳遞了兩條消息。
GrandFather.vue
<template>
<div class="parent">
🎈爺爺
<Father :msg1="msg1" :msg2="msg2" />
</div>
</template>
<script>
import Father from './Father'
export default {
components: {Father},
data () {
return {
msg1: '1️⃣我是GrandFather,把第二條傳給GrandSon',
msg2: '2️⃣GrandSon你好,我是GrandFather'
}
}
}
</script>
② Father 中使用 props 接收了來自 GrandFather 的所有消息。是的,他把所有的消息都收下了而且還可以隨便看😱。
當然使用 props 鏈傳遞就必須要 Father 接收之后才能繼續傳遞。
看完消息之后,Father 按照 GrandFather 的意思,把 msg2 傳遞給了 GrandSon
Father.vue
<template>
<div class="parent">
🎈父親
<p>GrandFather說:{{msg1}}。{{msg2}}</p>
<GrandSon :msg2="msg2" />
</div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
props: ['msg1', 'msg2'],
components: {GrandSon},
}
</script>
③ 終於到 GrandSon 了,它通過 props 從 Father 那里接收到了來自 GrandFather 的消息。
GrandSon.vue
<template>
<div class="child">
🎈孫子
<p>GrandFather說:{{msg2}}</p>
</div>
</template>
<script>
export default {
props: ['msg2']
}
</script>
小結
這種方式雖然是比較容易理解,但也是比較繁瑣的。中間組件需要接收所有的 props 等。
2 $attrs
上面的 $props 傳值方式必須要經過 Father 接收之后繼續傳遞,也是個缺點,畢竟 Father 還是很忙的,要負責自己的功能,不能總為爺孫倆接傳消息😂。
vue 在 2.4.0 版本中新增了 $attrs 屬性。根據前面的理解 $attrs 就是沒有在 props 中聲明要接收的一些屬性。此外,還可以通過 v-bind="$attrs" 把來自父組件的一些屬性直接傳遞到子組件中。
這樣一來,Father組件就沒必要在 props 中聲明接收那些不必要屬性了。看看實例吧!
① GrandFather 組件不用做修改
② 這次在 Father 中只在 props 接收了 msg1,與自己無關的直接使用 v-bind="attrs" 綁定到子組件上。
當然,在 Father 中還是可以訪問 $attrs 的。在代碼中訪問要使用 this.$attrs
Father.vue
<template>
<div class="parent">
🎈父親
<p>$attrs:{{$attrs}}</p>
<GrandSon v-bind="$attrs" />
</div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
props: ['msg1'], //只接收了msg1
components: {GrandSon},
}
</script>
③ 子組件也不需要做修改
在 Father 中接收了 msg1,所以在 Father 中繼續傳遞的就只有 msg2 了。
孫傳祖
$listeners
給 Father 組件綁定 自定義事件 getReply ,便於后面在 GrandSon 中觸發
GrandFather.vue
<template>
<div class="parent">
🎈爺爺
<div>GrandSon的回復:{{reply}}</div>
<Father :msg1="msg1" :msg2="msg2" @getReply="getReply"/>
</div>
</template>
<script>
import Father from './Father'
export default {
components: {Father},
data () {
return {
msg1: '1️⃣我是GrandFather,把第二條傳給GrandSon',
msg2: '2️⃣GrandSon你好,我是GrandFather',
reply: '' //接收來自GrandSon的消息
}
},
methods: {
/* 將獲得的數據綁定到data中,便於視圖層渲染 */
getReply (param) {
this.reply = param
}
}
}
</script>
在 Father 中使用 v-on="$listeners" 把 GrandFather 的事件綁定到 GrandSon
Father.vue
<template>
<div class="parent">
🎈父親
<p>$attrs:{{$attrs}}</p>
<GrandSon v-bind="$attrs" v-on="$listeners" />
</div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
props: ['msg1'],
components: { GrandSon },
}
</script>
在 GrandSon 中觸發來自 GrandFather 的自定義事件就 🆗 了,有兩種方式。
① this.$ listeners.eventName(param)
② this.$emit(eventName, param)
GrandSon.vue
<template>
<div class="child">
🎈孫子
<p>GrandFather說:{{msg2}}</p>
<button @click="reply">回復GrandFather</button>
</div>
</template>
<script>
export default {
props: ['msg2'],
data () {
return {
replyWord: 'GrandFather你好,我是GrandSon,收到消息了'
}
},
methods: {
reply () {
this.$emit('getReply', this.replyWord)
// this.$listeners.getReply(this.replyWord)
}
}
}
</script>
關於
關於Vue組件之間的傳值還有更多的方法,有時間學習吧!