有時候我們需要父元件直接通路子元件,子元件直接通路父元件,或者是子元件通路根元件。
官方指出:
1)父元件通路子元件:使用$children或$refs;
2)子元件通路父元件:使用$parent。
1、父元件通路子元件:$children
1)this.$children是一個數組,它包含所有子元件對象;
2)我們這裡通過一個周遊,取出所有子元件的message狀态。
<div id="app"> <parent-cpn>parent-cpn>div><template id="parentCpn"> <child-cpn1>child-cpn1> <child-cpn2>child-cpn2> <button @click="showMessage">顯示所有子元件資訊<button>template><template id="childCpn1"> <h2>我是子元件一<h2>template><template id="childCpn2"> <h2>我是子元件二<h2>template>Vue.component("parent-cpn",{ template:"#parentCpn", methods:{ showMessage(){ for(let i=0; i<this.$children.length; i++){ console.log(this.$children[i].message); } } })
說明:這裡的message沒有寫出來,message分别在兩個子元件的data裡進行定義。這裡通過this.$children周遊通路了兩個子元件的message。
2、父元件通路子元件:$refs
上面使用的$children存在缺陷:
1)$children是數組類型,通路其中的子元件必須通過索引值;
2)但是當子元件過多時,我們需要拿到其中一個子元件往往不能确定它的索引值。
有時候,我們想明确擷取其中一個特定的子元件,這個時候就可以使用$refs。
$refs的使用:
$refs和ref指令是一起使用的。
1)首先,我們通過ref給某一個子元件綁定一個特定的ID;
2)其次,通過this.$refs.ID就可以通路到該子元件了。
說明:上面所說的ID對應child1和child2,通路的時候通過this.$refs.ID的形式。
3、子元件通路父元件:$parent
如果我們想在子元件中直接通路父元件,可以通過$parent。
注意事項:
盡管在Vue開發中,允許通過$parent來通路父元件,但是在真實開發中盡量不要這樣做。 子元件應該盡量避免直接通路父元件的資料,因為這樣耦合度太高了。如果我們将子元件放在另外一個元件(父元件)之内,很可能該父元件沒有對應的屬性,往往會引起問題。 另外,更不應該做的是通過$parent直接修改父元件的狀态,因為這樣做的話父元件中的狀态将變得飄忽不定,很不利于我們的調試和維護。
<div id="app"> <parent-cpn>parent-cpn>div><template id="parentCpn"> <child-cpn>child-cpn>template><template id="childCpn"> <button @click="showParent">顯示父元件資訊<button>template>Vue.component("parent-cpn",{ template:"#parentCpn", data(){ return {message:"我是父元件"} }, components:{ "child-cpn",{ template:"#childCpn", methods:{ showParent(){ console.log(this.$parent.message); } } } }})
說明:通過this.$parent通路父元件的message。
4、非父子元件之間的通信:中央事件總線和Vuex
上面說的都是父子元件間的通信,那如果是非父子元件關系呢? 非父子元件關系包括多個層級的元件關系,也包括兄弟元件的關系。在Vue1.x的時候,可以通過$dispatch和$broadcast完成:
1)$dispatch用于向上級派發事件;
2)$broadcast用于向下級廣播事件。
但是在Vue2.x都被取消了,在Vue2.x中的方案是通過中央事件總線Bus,也就是一個中介來完成。通常是将Bus挂載到Vue根執行個體對象中使用,或者将Bus抽離成Bus.js的形式通過引入的方式使用。
方式一:挂載到Vue根對象中。在子元件中通過this.$root.Bus.$emit()觸發,通過this.$root.Bus.$on()監聽。
import Vue from 'vue';const Bus = new Vue();var app= new Vue({ el:'#app', data:{ Bus }})
方式二:将Bus抽離成Bus.js。同樣是在一個元件中通過this.$root.Bus.$emit()觸發,通過this.$root.Bus.$on()監聽。
Bus.jsimport Vue from 'vue';const Bus = new Vue();export default Bus//元件1import Bus from './Bus';export default { data() { return { ...... } }, methods: { ...... Bus.$emit('log', 120); }}//元件二import Bus from './Bus';export default { data() { return { ...... } }, mounted() { Bus.$on('log', content => { console.log(content); }); }}
但是這種方案比直接使用Vuex狀态管理遜色很多,并且Vuex提供了更多好用的功能,是以這裡暫且不讨論事件總線Bus這種方案,後續會進行Vuex狀态管理的說明。