這個數據流是單向的。數據流向是從父組件傳到子組件。也就是說,父級 prop
的更新會向下流動到子組件中,可是反過來則不行。
如今咱們須要寫一個博文組件,展現博文的標題和內容。標題和內容這些數據是從父組件得到的。
第一步,定義一個組件,這裏用註冊全局組件的方式,子組件用prop接收來自父組件的數據:html
Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h2>{{ post.title }}</h2> <div v-html="post.content"></div> </div> ` });
第二步,初始化一個Vue實例並掛載到對應的HTML結構:vue
// html <div id="app"> <blog-post :post="post" v-for="(post, index) in postArr" :key="index"></blog-post> </div> // js new Vue({ el: "#app", data: { postArr: [ { title: 'My name is Judy', content: 'something here ' }, { title: 'My job is coding', content: 'something here ' }, { title: 'I like coding', content: 'something here ' } ] } });
demo:父組件經過 Prop 向子組件傳遞數據vuex
在咱們開發 <blog-post>
組件時,它的一些功能可能要求咱們和父級組件進行溝通。例如咱們可能會引入一個可訪問性的功能來放大博文的字號,同時讓其餘部分的博文保持默認的字號。
仍是剛纔那個demo,在其父組件中,咱們能夠經過給每一條博文的數據添加一個 postFontSize
數據屬性來控制每篇博文字體的大小。app
// js new Vue({ el: "#app", data: { postArr: [ { postFontSize: 1, title: 'My name is Judy', content: 'something here ' }, { postFontSize: 1, title: 'My job is coding', content: 'something here ' }, { postFontSize: 1, title: 'I like coding', content: 'something here ' } ] } });
如今咱們須要給子組件添加一個按鈕。當點擊這個按鈕時,咱們須要告訴父級組件放大全部博文的文本。恰好 Vue 實例提供了一個自定義事件的系統來解決這個問題。咱們能夠調用內建的 $emit
方法並傳入事件的名字,來向父級組件觸發一個事件。改寫一會兒組件:ide
Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h2>{{ post.title }}</h2> <button @click="$emit('enlarge-text')"> Enlarge text </button> <div v-html="post.content"></div> </div> ` });
而後咱們能夠用 v-on 在博文組件上監聽這個事件,就像監聽一個原生 DOM 事件同樣:post
<div id="app"> <blog-post @enlarge-text="post.postFontSize += 0.1" :post="post" v-for="(post, index) in post_arr" :key="index" :style="{ fontSize: post.postFontSize + 'em' }" ></blog-post> </div>
demo:子組件經過事件向父級組件發送消息字體
以上爲經常使用的父組件向子組件傳遞數據、子組件經過事件向父級組件發送消息的兩種方式。ui
$parent
屬性能夠用來從一個子組件訪問父組件的實例。它提供了一種機會,能夠在後期隨時觸達父級組件,以替代將數據以 prop 的方式傳入子組件的方式,實際上這不是一種數據傳遞,而是子組件主動發起的數據查找。
可是Vue並不推薦這麼作。在絕大多數狀況下,觸達父級組件會使得咱們的應用更難調試和理解,尤爲是咱們變動了父級組件的數據的時候。當咱們稍後回看那個組件的時候,很難找出那個變動是從哪裏發起的。
在一些可能適當的時候,咱們須要特別地共享一些組件庫,就能夠考慮用$parent
。Vue官網稱這些狀況爲【邊界狀況】。
假設有一個Google地圖組件:this
<google-map> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map>
這個 <google-map> 組件能夠定義一個 map
屬性,全部的子組件都須要訪問它。在這種狀況下 <google-map-markers>
能夠經過相似 this.$parent.getMap
的方式訪問那個地圖,以便爲其添加一組標記。
點擊這裏查看官方文檔給出的demogoogle
儘管存在 prop
和事件,有的時候仍可能須要在 JavaScript 裏直接訪問一個子組件。爲了達到這個目的,咱們能夠經過 ref
特性爲這個子組件賦予一個 ID 引用。
例如:
<base-input ref="usernameInput"></base-input>
如今在已經定義了這個 ref
的組件裏就可使用:
this.$refs.usernameInput
來訪問這個 <base-input> 實例,以便不時之需。
另外,vm.$children
也能夠訪問當前實例的直接子組件。須要注意 $children
並不保證順序,也不是響應式的。
當咱們須要訪問子組件或者子元素,推薦使用 ref
而不是 $children
。
由於 ref
能幫咱們訪問到的是任意子組件實例或者子元素,$children
只能訪問到直接子組件。
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。
什麼意思呢?在實際開發中,咱們會遇到有一處或者多處須要被多個實例間共享的狀態,Vuex就是爲咱們提供了這樣一個模式。因此個人理解是Vuex不只僅是適用於父子組件的通訊了,它適用於全部組件共享某些必要的數據狀態。
關於Vuex的使用咱們這裏不詳細展開討論了,貼一個Vuex的文檔地址。
前面咱們提到google地圖組件,相似這樣的:
<google-map> <google-map-region v-bind:shape="cityBoundaries"> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map-region> </google-map>
在這個組件裏,全部 <google-map> 的後代都須要訪問一個 getMap
方法,以便知道要跟那個地圖進行交互。不幸的是,使用 $parent
屬性沒法很好的擴展到更深層級的嵌套組件上,也就是說,只有<google-map-region>
能經過 $parent
訪問到它的直接父組件<google-map>的 getMap 方法。這種狀況能夠用依賴注入 provide
和 inject
解決。
provide
選項容許咱們指定咱們想要提供給後代組件的數據/方法。在這個例子中,就是 <google-map>
內部的 getMap
方法:
provide: function () { return { getMap: this.getMap } }
而後在任何後代組件裏,咱們均可以使用 inject
選項來接收指定的咱們想要添加在這個實例上的屬性:
inject: ['getMap']
相比 $parent
來講,這個用法可讓咱們在任意後代組件中訪問 getMap
,而不須要暴露整個 <google-map>
實例。這容許咱們更好的持續研發該組件,而不須要擔憂咱們可能會改變/移除一些子組件依賴的東西。同時這些組件之間的接口是始終明肯定義的,就和 props
同樣。
實際上,你能夠把依賴注入看做一部分「大範圍有效的 prop
」,除了:
我看了不少篇總結Vue父子組件通訊方式的博客都沒有提到依賴注入,不過我以爲依賴注入也實現了通訊——由父組件經過 provide
把數據暴露出去,子組件經過 inject
接收數據。
以上就是我閱讀官方文檔總結出來的Vue組件通訊的幾種方式,大部份內容和demo都來源於官網文檔,瞭解更多能夠閱讀文檔:Vue文檔地址。文章所有內容僅表明我的觀點,歡迎批評或者與我討論。感謝你的閱讀。