1. vm.$root
1.1 描述
當前組件樹的根 Vue 實例。如果當前實例沒有父實例,此實例將會是其自己。
在Vue 中, 什麼是一個組件樹的根實例? 實際上,通過Vue Devtools 能夠很清楚的看到:
具體的,在代碼中,Vue 項目的入口文件main.js
中,一般的會有這樣的代碼段:
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
創建了一個Vue實例,並塞入了一些東西后,通過$mount
掛載到了#app
這個dom節點。
實際上,和我們常用的組件化開發一樣,我們也能在創建組件時(即便是根組件),也能夠通過一個個工廠函數的形式,定義一些數據作爲這個組件的動態屬性值。 例如:
new Vue({
data() {
return {
name: "$root",
};
},
router,
store,
render: (h) => h(App),
}).$mount("#app");
這樣,你便能夠在全局範圍內,在組件樹的任何一個節點,通過vm.$root
獲取到這個根實例,也就能以vm.$root.name
的形式訪問到我們定義的name
屬性。
1.2 能力
我們可以發現,這點很像一箇中央數據管理對象, 類似localStorage,或者更加類似於store,或者Vuex。 沒錯,只是取決於項目的複雜度考慮,通常不會這樣去使用。 但是如果項目本身比較小,而無需引入Vuex 的情況下。 我們可以考慮這樣去實現需求。
並且 ,由於data
對象中的所有的 property 都會被加入到 Vue 的響應式系統中, 這意味着,這個值的變化將會引起所有引用處的dom渲染更新。 即:
<!--組件樹中的任意組件a-->
vm.$root.name = "update name value!"
<!--組件樹中的任意組件b-->
<template>
{{$root.name}}
</template>
只要在組件樹中,任意組件a中去修改name
值,其他任意引用的組件都會自動更新。
2. $parent
和$children
2.1 描述
$parent
指代父實例,如果當前實例有的話。
而$children
指代子實例,如果當前實例有的話。
所不同的是,也值得注意的是這二者的類型不同,$children
的類型是 Array<Vue instance>
, 而 $parent
的類型是 Vue instance
一個組件可以有多個子組件,因此 $children
的類型是 Array<Vue instance>
, 這是易於理解的,但是,爲什麼 $parent
的類型是 Vue instance
而不是 Array<Vue instance>
呢?
一個組件不是也可以被多個組件引用嗎? 那父組件不是也應該有多個嗎?,如果我們畫一個圖來理解:
其實,這個原因,文檔中早有提及:
注意當點擊按鈕時,每個組件都會各自獨立維護它的
count
。因爲你每用一次組件,就會有一個它的新實例被創建。link
組件被複用時,爲了不會相互影響,Vue 實際上在背後爲我們創建了一個全新的實例。
以下是一個驗證demo:
2.2 能力
通過這兩個屬性,我們能夠在兩個嵌套關係的組件中,輕鬆的從子組件訪問父組件實例,或者從父組件訪問子組件實例。
3. 通過$ref
訪問子組件實例或者元素
3.1描述:
通過ref
給一個子組件標記一個"id", 然後當子組件掛載後,便可以通過$ref.refname
的形式訪問到對應的子組件實例。
在項目開發中,這是一個常使用的子組件實例訪問方式。
以下是一個使用示例。
3.2 能力
通過$refs
,vue又給了我們一種訪問組件實例的能力,在一定場景下靈活運用,能極大的方便我們的開發過程。
3.3 注意點
-
要注意使用的所在生命週期
在使用ref這種方式時,你特別需要注意的是,你僅能在該組件掛載完畢後才能訪問到該組件實例,所有在使用時,你需要確保組件已經掛載的情況下,再使用。
一般來說,父子組件的
created
和mounted
聲明週期通常是這樣的:
父組件 created --> 子組件 created --> 子組件 mounted --> 父組件mounted
這意味着,如果你不加以注意的在父組件的
created
聲明週期鉤子種去通過ref 訪問子組件實例,是不會成功的:如果你嘗試通過ref 訪問一個根元素上有
v-if
的組件元素,通常是會遇到一些問題的。 因爲一些場景下,v-if
的接入會導致上面提到的常規父子生命週期鉤子的執行順序失常。 爲了解決該問題,通常你可以利用$nextTick
。 -
不要依賴ref實現響應式
$refs
只會在組件渲染完成之後生效,並且它們不是響應式的。這僅作爲一個用於直接操作子組件的“逃生艙”——你應該避免在模板或計算屬性中訪問$refs
。例如,這樣是不能預期運行的:
4. 依賴注入
4.1 描述
假如,有一個多級嵌套的組件,而你需要在最內部去調用最外層組件的方法,你會怎麼辦? 先不考慮這樣的組件是否合適,也不考慮更好的抽離方法。
那麼,首先容易想到的是,我們首先要獲取到這個最外層的組件的實例,才能去調用最外層組件中methods 對象中定義的方法。
前面我們剛提到過$parent
可以獲取到父組件的實例。 那麼如果層級比較淺還好,我們通過 this.$parent.$parent.....xxxfunc()
這樣的"鏈式訪問"就能達到目的了。
但是顯然,這是不利於維護的。 也是不太聰明的。
因爲一旦你中間需要多嵌套一級組件,那麼你就需要對剛纔的”鏈式訪問加一個$parent
“。
爲了說的更加清楚,我們寫一個簡單的demo復現一下這種場景:
簡單說明一下,在index.vue
中,我們引入了LevelTwo
和 LevelThree
兩個組件,並且寫作了嵌套的形式。
之所以能寫作嵌套的形式,是利用了
<slot>
,如果你對這裏不太清楚,請回顧前面的插槽內容。這裏只提一點:
如果LevelTwo
組件定義中,沒有包含一個<slot>
元素,則該組件<LevelTwo>
起始標籤和</LevelTwo>
結束標籤之間的任何內容都會被拋棄。
這樣,就構成了一個三級嵌套的組件層級關係。即:
<index>
└── <LevelTwo>
└── <LevelThree>
那麼,我們在<LevelTwo>
中去調用時,就是這樣去調用的:
this.$parent.indexFunc();
同理,在<LevelThree>
,是這樣調用的:
this.$parent.$parent.indexFunc();
如果,嵌套層級更深會很不方便,且一旦有層級上的改動,這裏都要動。
4.2 通過依賴注入,解決特定場景下的問題
首先,這種場景並不常見。 因爲一般有公共方法,我們第一想法應該是把它抽離出去。然後在需要使用的地方去引入。那麼這裏vue,也爲這種特定場景,提供瞭解決方案,那就是 依賴注入。
其實,易於理解,我們把子組件中可能用到的方法,通過某種方式給他"註冊"一下。
然後在需要用到該方法的子組件中,去"引入"一下。
我們直接通過示例認識一下用法:
【簡單描述】:
Vue 爲我們提供兩個新的實例選項:provide
和 inject
。
provide
選項允許我們指定我們想要提供給後代組件的數據/方法。也就是我們剛纔提到的"註冊"
provide: function() {
return {
indexFunc: this.indexFunc,
};
},
然後在需要用到的所有子組件中,通過inject
"注入" provide
的數據/方法, 也就是我們剛纔提到的"引入"
最後,直接通過this.indexFunc()
就可以成功調用了。
這就是依賴注入。
vue 文檔上用的示例是google map , 對國內用戶確實不太友好,因爲假如你想寫個google map 的demo , 你還得搞到google map 開發的key纔行 - -