轉自Vue案例引發的「巢狀元件」通訊的簡單方式
我們都知道 Vue 是採用元件化開發的模式,元件化的優勢在於相對獨立,易於維護,可複用。你可以把專案看成許多元件的組合而成。
既然專案中存在很多的元件,而且又是相對獨立的,但元件間肯定是存在資料的傳遞互動。Vue中給我提供比較多的方式去進行元件間的互動通訊。
這篇文章不打算詳盡元件之間的通訊,而是說說利用 $attrs
與 $listeners
進行「巢狀元件」的通訊。
可以想象一下專案中元件與元件的關係無外乎這麼幾種:父子,兄弟,祖孫(巢狀)。
- 父子元件:父元件通過 props 向下傳遞子元件資料,子元件通過事件向上傳送父元件訊息。或者也可以通過 ref 屬性、
$parent
、$children
等方法獲取資料和事件。 - 兄弟元件:可以通過共同的父元件作為橋樑進行通訊,也可以利用全域性事件 eventBus 或者比較複雜的 Vuex。
一圖勝千言
通過上圖,我們可以瞭解常見的元件通訊方式。但實際的開發專案中可能並沒有這麼簡單,最近在做專案時遇到巢狀元件的情況,比如「元件A」包含「元件B」,「元件B」包含「元件C」。
那「元件A」與「元件C」如何通訊就是值得我們商榷的問題,是利用 Vuex 還是利用其他方式呢?
首先 Vuex 是優秀的狀態管理工具,對於複雜而又龐大的系統而言使用 Vuex 再好不過。
但如果我們的系統相對簡單,並且「元件A」與「元件C」之間只是進行簡單的資料傳遞,似乎引入 Vuex 並不是一個好的選擇,相反會帶來複雜度的上升。
不過 Vue 在 2.4.0 版本新增了 2 個屬性$attrs
與$listeners
,使用它們進行巢狀元件(祖孫)的通訊是一個不錯的選擇,接下來我們就看看它們是什麼,以及如何使用。
1.$attrs
官方解釋:包含了父作用域中不作為 prop 被識別 (且獲取) 的特性繫結 (class 和 style 除外)。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部元件——在建立高階別的元件時非常有用。
2.$listeners
包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部元件——在建立更高層次的元件時非常有用。
簡單來說:$attrs
與 $listeners
是兩個「物件」,$attrs
裡存放的是父元件中繫結的非 Props 屬性,$listeners
裡存放的是父元件中繫結的非原生事件。
在不明白的話看個案例
//componentA
<template>
<div class="component-a">
<component-b :name="name" :tag="tag" :age="age" @click.native="say" @mouseover="sing"></component-b>
</div>
</template>
<script>
import componentB from "./ComponentB";
export default {
name: "componentA",
components: { componentB },
data() {
return {
name: "六哥",
tag: "帥",
age: 18
};
},
methods: {
say() {},
sing() {}
}
};
</script>
//componentB
<template>
<div class="component-b"></div>
</template>
<script>
export default {
name: "ComponentB",
props: {
age: Number
},
mounted() {
console.log(this.$attrs, this.$listeners);
//{name: "六哥", tag: "帥"}, {mouseover: ƒ}
}
};
</script>
複製程式碼
明白這兩個屬性之後,我們來看看如何利用它進行祝祖孫元件之間的傳遞。假如有三個元件分別是「元件A」包含「元件B」,「元件B」包含「元件C」。
在上面案例的基礎上,我們新增「元件C」,並對「元件B」進行改善。
//componentB
<template>
<div class="component-b">
<component-c v-bind="$attrs" v-on="$listeners"></component-c>
</div>
</template>
<script>
import ComponentC from "./ComponentC";
export default {
name: "ComponentB",
components: { ComponentC }
};
</script>
//componentC
<template>
<div class="component-c"></div>
</template>
<script>
export default {
name: "ComponentC",
mounted() {
console.log(this.$attrs, this.$listeners);
//{name: "六哥", tag: "帥", age: 18} {mouseover: ƒ}
}
};
</script>
複製程式碼
可以看到我們利用$attrs
與$listeners
在不引入額外的工具或者全域性屬性,就可以實現從「元件A」到「元件C」之間的資料通訊。如果有相同場景的小夥伴,趕緊用起來吧。
另外一點,這裡需要注意我們使用了非 Props 特性,Vue 中元件如果接受非 Props 屬性的時候,會把屬性渲染到 HTML 的原生標籤上。
例如:
<component-b :name="name" :tag="tag"></component-b>
複製程式碼
渲染後的結果會是
<div class="component-b" name="六哥" tag="帥"></div>
複製程式碼
顯然這不是我們想要的,我們的原生標籤不需要 「name」與 「tag」這兩個屬性。那如何避免的呢?很簡單,你可以在元件的選項中設定 inheritAttrs: false。
<script>
import ComponentC from "./ComponentC";
export default {
inheritAttrs: false,
name: "ComponentB",
components: { ComponentC }
};
</script>
複製程式碼
以上就是我們今天要說的 $attrs
與$listeners
.