寫在前面
組件間的通信是是實際開發中非常常用的一環,如何使用對項目整體設計、開發、規範都有很實際的的作用,我在項目開發中對此深有體會,總結下vue組件間通信的幾種方式,討論下各自的使用場景
文章對相關場景預覽
- 父->子組件間的數據傳遞
- 子->父組件間的數據傳遞
- 兄弟組件間的數據傳遞
- 組件深層嵌套,祖先組件與子組件間的數據傳遞
文章相關技術預覽
prop、emit、bus、vuex、路由URL、provide/inject
注:以下介紹與代碼環境:vue2.0+、vue-cli2
父->子組件間的數據傳遞
父子組件的通信是開發是最常用的也是最重要的,你們一定知道父子通信是用prop傳遞數據的,像這樣:
//父組件,傳遞數據
<editor :inputIndex="data" :inputName="王文健"></editor>
//子組件,接受數據,定義傳遞數據的類型type與默認值default
props: {
inputIndex: {
type: Object,
default: function(){
return {}
}
},
inputName: {
type: String,
default: ''
},
注意項:
-
父組件傳遞數據時類似在標籤中寫了一個屬性,如果是傳遞的數據是data中的自然是要在傳遞屬性前加v-bind:,如果傳遞的是一個已知的固定值呢
- 字符串是靜態的可直接傳入無需在屬性前加v-bind
- 數字,布爾,對象,數組,因爲這些是js表達式而不是字符串,所以即使這些傳遞的是靜態的也需要加v-bind,把數據放到data中引用,
如果prop傳到子組件中的數據是一個對象的話,要注意傳遞的是一個對象引用,雖然父子組件看似是分離的但最後都是在同一對象下
- 如果prop傳到子組件的值只是作爲初始值使用,且在父組件中不會變化賦值到data中使用
- 如果傳到子組件的prop的數據在父組件會被改變的,放到計算屬性中監聽變化使用。因爲如果傳遞的是個對象的話,只改變下面的某個屬性子組件中是不會響應式更新的,如果子組件需要在數據變化時響應式更新那隻能放到computed中或者用watch深拷貝deep:true才能監聽到變化
- 當然如果你又需要在子組件中通過prop傳遞數據的變化做些操作,那麼寫在computed中會報警告,因爲計算屬性中不推薦有任何數據的改變,最好只進行計算。如果你非要進行數據的操作那麼可以把監聽寫在watch(注意deep深拷貝)或者使用computed的get和set如下圖:
- 但問題又來了,如果你傳進來的是個對象,同時你又需要在子組件中操作傳進來的這個數據,那麼在父組件中的這個數據也會改變,因爲你傳遞的只是個引用, 即使你把prop的數據複製到data中也是一樣的,無論如何賦值都是引用的賦值,你只能對對象做深拷貝創建一個副本才能繼續操作,你可以用JSON的方法先轉化字符串在轉成對象更方便一點,
- 所以在父子傳遞數據時要先考慮好數據要如何使用,否則你會遇到很多問題或子組件中修改了父組件中的數據,這是很隱蔽並且很危險的
子->父組件間的數據傳遞
在vue中子向父傳遞數據一般用$emit自定義事件,在父組件中監聽這個事件並在回調中寫相關邏輯
// 父組件監聽子組件定義的事件
<editor :inputIndex="index" @editorEmit='editorEmit'></editor>
// 子組件需要返回數據時執行,並可以傳遞數據
this.$emit('editorEmit', data)
那麼問題來了,我是不是真的有必要去向父組件返回這個數據,用自定義事件可以在當子組件想傳遞數據或向子組件傳遞的數據有變化需要重新傳遞時執行,那麼另外一種場景,父組件需要子組件的一個數據但子組件並不知道或者說沒有能力在父組件想要的時候給父組件,那麼這個時候就要用到組件的一個選項ref:
<editor ref="editor" @editorEmit='editorEmit'></editor>
- 父組件在標籤中定義ref屬性,在js中直接調用this.$refs.editor就是調用整個子組件,子組件的所有內容都能通過ref去調用,當然我們並不推薦因爲這會使數據看起來非常混亂,
- 所以我們可以在子組件中定義一種專供父組件調用的函數,,比如我們在這個函數中返回子組件data中某個數據,當父組件想要獲取這個數據就直接主動調用ref執行這個函數獲取這個數據,這樣能適應很大一部分場景,邏輯也更清晰一點
- 另外,父向子傳遞數據也可以用ref,有次需要在一個父組件中大量調用同一個子組件,而每次調用傳遞的prop數據都不同,並且傳遞數據會根據之後操作變化,這樣我需要在data中定義大量相關數據並改變它,我可以直接用ref調用子組件函數直接把數據以參數的形式傳給子組件,邏輯一下子清晰了
- 如果調用基礎組件可以在父組件中調用ref執行基礎組件中暴露的各種功能接口,比如顯示,消失等
兄弟組件間的數據傳遞
vue中兄弟組件間的通信是很不方便的,或者說不支持的,那麼父子組件中都有什麼通信方式呢
-
路由URL參數
-
在傳統開發時我們常常把需要跨頁面傳遞的數據放到url後面,跳轉到另外頁面時直接獲取url字符串獲取想要的參數即可,在vue跨組件時一樣可以這麼做,
// router index.js 動態路由 { path:'/params/:Id', component:Params, name:Params }
// 跳轉路由 <router-link :to="/params/12">跳轉路由</router-link>
- 在跳轉後的組件中用$route.params.id去獲取到這個id參數爲12,但這種只適合傳遞比較小的數據,數字之類的
-
- Bus通信
在組件之外定義一個bus.js作爲組件間通信的橋樑,適用於比較小型不需要vuex又需要兄弟組件通信的
-
bus.js中添加如下
import Vue from 'vue' export default new Vue
-
組件中調用bus.js通過自定義事件傳遞數據
import Bus from './bus.js' export default { methods: { bus () { Bus.$emit('msg', '我要傳給兄弟組件們') } } }
-
兄弟組件中監聽事件接受數據
import Bus from './bus.js' export default { mounted() { Bus.$on('msg', (e) => { console.log(e) }) } }
注:以上兩種使用場景並不高所以只是簡略提一下,這兩點都是很久以前寫過,以上例子網上直接蒐集而來如有錯誤,指正
- Vuex集中狀態管理
vuex是vue的集中狀態管理工具,對於大型應用統一集中管理數據,很方便,在此對vuex的用法並不過多介紹只是提一下使用過程中遇到的問題
-
規範:對於多人開發的大型應用規範的制定是至關重要的,對於所有人都會接觸到的vuex對其修改數據調用數據都應有一個明確嚴格的使用規範
- vuex分模塊:項目不同模塊間維護各自的vuex數據
- 限制調用:只允許action操作數據,getters獲取數據,使用mapGetters,mapActions輔助函數調用數據
- 對於vuex的使用場景也有一些爭論,有人認爲正常組件之間就是要用父子組件傳值的方式,即使子組件需要使vuex中的數據也應該由父組件獲取再傳到子組件中,但有的時候組件間嵌套很深,只允許父組件獲取數據並不是一個方便的方法,所以對於祖先元組件與子組件傳值又有了新問題,vue官網也有一些方法解決,如下
祖先組件與子組件間的數據傳遞
provide/inject
除了正常的父子組件傳值外,vue也提供了provide/inject
這對選項需要一起使用,以允許一個祖先組件向其所有子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效
官網實例
// 父級組件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子組件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
}
- provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。
-
一個字符串數組,或
一個對象,對象的 key 是本地的綁定名,value 是:-
在可用的注入內容中搜索用的 key (字符串或 Symbol),或
一個對象,該對象的:- from 屬性是在可用的注入內容中搜索用的 key (字符串或 Symbol)
- default 屬性是降級情況下使用的 value
-
提示:provide 和 inject 綁定並不是可響應的。這是刻意爲之的。然而,如果你傳入了一個可監聽的對象,那麼其對象的屬性還是可響應的。
具體細節移步vue相關介紹https://cn.vuejs.org/v2/api/#...
provide/inject還未在項目中應用過,後面會做嘗試
寫在結尾
文章只是整理一下筆記,談一談遇到的問題和經驗,並沒有嚴謹的措辭和詳細的過程,如有錯誤望指正
原創文章轉載引用請註明原文鏈接http://blog.wwenj.com/index.p...