Vue中實現組件之間的通訊方式有不少種,eventBus,props,vuex,v-on,ref...等等。本身在不少時候用的沒有章法,簡單的想着實現功能就能夠了,以致於邏輯混論,後期很難維護。因此給本身作個總結,方法並非惟一,只是我如今認爲的解決方案。html
組件通訊包括:子組件與父組件之間,兄弟組件之間,模塊之間vue
props是響應式的,能夠作數據綁定
index.vue父組件相關的代碼
若是傳給子組件的是一個變量或者數字,則須要前面加上:(v-bind的縮寫)綁定。
這邊的imgHeight是一個變量,closeFuction是一個方法vuex
<template> <div> <child :img-width="344" :img-height="imgHeight" title="靜態文字" :before-close="closeFuction"></child> </div> </template>
child.vue子組件中的,使用的話直接能夠用 this.imgWidth形式獲取到。npm
props:{ imgWidth: { type: Number, default: 300 }, imgHeight: { type: Number }, title: { type: String, default: '' }, beforeClose: { type: Function, default: function() { console.warn('need a init submitHandle Prop!'); } } }
由於html對於大小寫並不敏感,因此子組件中駝峯命名在父組件中建議採用下劃線。傳遞給子組件的方法相似於一個回調函數。調用以後能夠更改父組件中的內容,也算是子組件向父組件通訊了。另外值得注意的是,根據Vue風格指南中建議的,prop 的定義應該儘可能詳細,至少須要指定其類型。api
在使用組件時,常常會用到v-model。官方解釋是 v-model 本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據。app
對於v-on 官網解釋是用在普通元素上時,只能監聽原生 DOM 事件。用在自定義元素組件上時,也能夠監聽子組件觸發的自定義事件。
因此前面的例子咱們改一改dom
index.vue父組件相關的代碼異步
<template> <div> <child :img-width="344" :img-height="imgHeight" title="靜態文字" @before-close="closeFuction"></child> </div> </template>
closeFuction是一個方法,帶參數ide
closeFuction(val) { console.log(val); // todo }
child.vue子組件函數
methods: { doSomething() { // todo const val = '須要傳給父組件的參數'; this.$emit('before-close', val); console.log('在須要調用的地方 用this.$emit觸發'); } }
理解v-on在組件上的機制,就能夠更好的認識到,爲何v-model僅僅是個語法糖。
ref 被用來給元素或子組件註冊引用信息。就是綁定在普通元素上,則獲取到這個DOM元素,如果綁定在組件上,則獲取到的是這個組件的Vue實例vm。
同一個vue中:
<div ref="child"></div>
mounted() { let domA = document.querySelector('[ref=child]'); let domB = this.$ref.child; console.log('domA與domB是等價的,可是用$ref能夠減小獲取dom節點的消耗') }
index.vue
<template> <div> <child ref="childName"></child> </div> </template> ---------- mounted(){ let childData = this.$ref.childName.$data.childData; let childMethod = this.$ref.childName.doSomething; console.log('均可以訪問到'); }
child.vue子組件
data(){ return { childData: 'xxxx' } }, methods: { doSomething() { // todo } }
注意:
vue更新數據是異步的,咱們須要等到DOM更新完成,因此使用$ref進行DOM操做的時候,須要放在created的$nextTick(() => {}),或者直接放在mounted。
由於綁定組件的話返回的是vm實例,因此參考實例屬性獲取想要的數據等。
$refs 也不是響應式的,所以不該該試圖用它在模板中作數據綁定。
兄弟組件通訊有兩種方法,eventBus,vuex。可是我跟願意將eventBus放在模塊之間的通訊來說。
當非父子組件之間通訊較多時,用eventBus很容易邏輯混亂,較難維護。vuex將狀態管理單獨拎出來,應用統一的方式進行處理,能夠理解爲組件間公用的一個全局對象。
使用Vuex
安裝
npm install --save vuex
其實通常來講,用到vuex的時候,業務邏輯都已經比較複雜,因此我就講我本身在用的時候,項目文件的處理。
store/index.js
import Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); console.log("必須引入vuex"); const store = new Vuex.Store({ state: { stateName: 'xxxx' }, mutations: { mutationsName(state, {params}) { state.stateName = params; console.log("只有在mutations中才能直接改變state中的值") } }, actions: { actionName({ state,commit}, {params}) { let actionParam = 'mmm'; commit('mutationsName', actionParam ); console.log(" 觸發mutation 方法要用commit分發,以此改變state"); } } }); export default store;
main.js
console.log("store爲實例化生成的"); import store from './store/index.js'; new Vue({ el: '#app', store, console.log("將store掛載到vue實例上") render: h => h(App) })
在組件中使用
child.vue js部分
import { mapActions, mapMutations, mapState } from 'vuex'; export default { computed: { ...mapState({ stateName }) }, methods: { ...mapActions(['actionName']), ...mapMutations(['mutationName']) console.log("使用輔助函數mapMutations直接將觸發函數映射到methods上") } // 接下來在實例中就能夠用this.stateName,this.actionName來調用 }
當兄弟組件不少,涉及到的處理數據龐大的時候,能夠用到vuex中的modules,使得結構更加清晰
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的狀態 store.state.b // -> moduleB 的狀態
vuex講細篇幅很長,更多更復雜的內容,參考官方教程
eventBus的原理是引入一個新的vue實例,而後經過分別調用這個實例的事件觸發和監聽來實現通訊和參數傳遞。
eventBus.js 通常會直接用公共一個文件來存放vue實例
import Vue from 'vue'; export default new Vue();
咱們在apple.vue中監聽一個事件
apple.vue
import eventBus from 'eventBus.js'; // 咱們在create鉤子中監聽方法 create(){ console.log("this.getTarget是一個帶參數的方法,可是這邊只要將二者關聯起來"); eventBus .$on('getTarget', this.getTarget); }, beforeDestroy() { console.log("組件銷燬前須要解綁事件。不然會出現重複觸發事件的問題"); bus.$off('getTarget', this.getTarget); }, methods: { getTarget(param) { // todo } }
在orange.vue中觸發
import eventBus from 'eventBus.js'; // 必須引入同一個實例 methods: { doSomething() { eventBus.$emit("getTarget", 22); console.log("向getTarget方法傳參22"); } }
eventBus其實很是方便,任何的組件通訊都能用它來完成。可是,咱們會根據狀況來選擇更易維護的方式。由於eventBus比較很差找到對應的監聽或者觸發事件具體實現的地方,因此通常組件通訊更考慮上面的實現方式。在模塊之間通訊利用eventBus,而後在模塊內部,利用vuex通訊,維護數據,會在邏輯上比較清晰。