之前簡單做了一次vue通信方法的培訓,在此記錄一下培訓的內容。
關於vue通信,大家最先想到的方法應該是props、ref、$emit、$parent,還有vuex,因為這也是我在項目中最常用到的方法,其實,還有其他的一些通信方法,下面我來作個簡單的介紹:
我主要通過組件類型來介紹:
一、父子組件通信:
(1)父向子傳值:
props: 一般用於父組件向子組件進行單向通信
ref:為子組件指定一個索引名稱,通過索引來操作子組件,用this.$refs.name 獲取到組件實例,可以使用組件的所有方法
$children: 父組件也可以通過this.$children 訪問它所有的子組件( 需要注意 $children 並不保證順序 )
(2)子向父傳值 :
$emit:觸發父組件的自定義事件 vm.$emit( event, arg )
$parent:通過this.$parent可以直接訪問該組件的父實例或組件(得到最近一級的父組件)
父子組件通信實例:
//父組件
<template> <div> <div class="box"> <a-button @click="onClick">傳參給子組件</a-button> 我是父組件,點擊次數:{{ msg }} <child ref="childRef" :msg="msg" @child-event="watchChildEvent"></child> </div> </div> </template> <script> import Child from "./son"; export default { components: { Child }, data() { return { msg: 0 }; }, methods: { onClick() { ++this.msg; }, //監聽由子組件觸發的事件,參數為子組件傳來的數據 watchChildEvent(val) { this.msg = val; }, //監聽由子組件觸發的事件,參數為子組件傳來的數據 watchParEvent(val) { this.parMsg = val; } }, mounted() { //父組件調用子組件方法 this.$refs.childRef.getMessage("我是子組件一"); } }; </script>
//子組件
<template> <div class="son-box"> <a-button @click="targetClick">通過$emit傳參到父組件</a-button><br /> <a-button @click="parClick">2、通過$parent傳參到父組件</a-button><br /> 我是子組件1,點擊次數:{{ msg }}<br /> {{ val }} </div> </template> <script> export default { components: {}, props: { msg: { type: [String, Number], //類型 default: 0 //可以設置默認值 } }, data() { return { val: "" }; }, methods: { //由子組件的事件觸發 自定義事件wathChildEvent targetClick() { this.$emit("child-event", 20); //參數改為this.msg ?? }, //使用$parent訪問父組件(得到最近一級的父組件) parClick() { this.$parent.watchParEvent("2、我是使用$parent更改的父級msg"); }, //由父組件的事件觸發 getMessage(m) { console.log(m); }, }, mounted() {} }; </script>
二、兄弟組件通信:
不使用 props 和 Vuex 時,可以用什么呢?
eventBus (中央事件總線event Bus),它允許兩個子組件之間直接通訊,而不需要涉及父組件。是實現非父子組件通信的一種解決方案。但是當項目較大較復雜時,並不適合。到那時,vuex才是vue給我們提供的最理想的方式。 原理:就是創建一個vue實例,通過一個空的vue實例作為橋梁實現vue組件間的通信。(在Vue2中可謂是最好用的非父子組件之間的通訊手段,但是Vue3宣布取消了,官方推薦的做法是讓用戶自己尋找使用第三方庫,后續有時間會分享一篇vue3如何去實現event Bus)
eventBus 的用法如下:
第一步:項目中創建一個js文件(命名為event-bus.js),引入vue,創建一個vue實例,導出這個實例,代碼如下(一共就兩行):
import Vue from 'Vue' export default new Vue
第二步:在兩個需要通信的兩個組件中分別引入這個event-bus.js
import Bus from '這里是你引入event-bus.js的路徑' // Bus可自由更換喜歡的名字
第三步:傳遞數據的組件里通過vue實例方法$emit發送事件名稱和需要傳遞的數據。(發送數據組件)
Bus.$emit('click',data) // 這個click是一個自定義的事件名稱,data就是你要傳遞的數據。
第四步:被傳遞數據的組件內通過vue實例方法$on監聽到事件和接受到數據。(接收數據的組件)這里通常掛載監聽在vue生命周期created和mounted當中的一個,具體使用場景需要具體的分析,這里不說這個。
Bus.$on('click',target => { console.log(target) //注意:發送和監聽的事件名稱必須一致,target就是獲取的數據,可以不寫target。(自己命名) })
通過以上的四步其實就已經實現了最簡單的eventbus的實際應用了。
但是到這兒后,一定要注意一個最容易忽視,又必然不能忘記的東西,那就是清除事件總線eventBus. 不手動清除,它是一直會存在的,這樣的話,有個問題就是反復進入到接受數據的組件內操作獲取數據,原本只執行一次的獲取的操作將會有多次操作。如上我所舉的例子,會打印多次傳過來的數據。但你想想,實際開發中是不會這么簡單的打印這個數據到控制台,本來只會觸發並只執行一次,現在變成了多次,這個問題就非常嚴重了,你們各種腦補具體的項目開發場景吧。
第五步:在vue生命周期beforeDestroy或者destroyed中用vue實例的$off方法清除eventBus
beforeDestroy(){ bus.$off('click') }
三、跨多層級組件通信
A-B-C,三個組件,從爺組件A傳參到孫組件C,應該怎么傳呢?
(1)通過props一層層傳遞過去,傳到子,給傳給孫
(2)用vuex 共享狀態(開發大型單頁應用)
(3)使用$attrs傳參,$listeners監聽事件,或者用provide 和 inject
1、$attrs和$listeners的介紹:
- $attrs是在vue的2.40版本以上添加的。
- 所有的非props屬性,都可以通過 v-bind="$attrs" 傳入內部組件——在創建高級別的組件時非常有用。
- 多層組件傳參使用$attrs,可以使代碼更加美觀,更加簡潔,維護代碼的時候更方便。如果使用普通的父子組件傳參prop會很繁瑣;如果使用vuex會大材小用,只是在這幾個組件中使用,沒必要使用vuex;使用事件總線eventBus,使用不恰當的話,有可能會出現事件多次執行。
- 所有組件上的方法綁定子組件都可以通過$listeners接收
- 詳情可查看:vue.js-$attrs
注意:
1、vue2中使用$attrs從父組件傳遞數據給子組件嵌套組件,父組件通過通過$listeners監聽子組件的事件
2、vue3把把$attrs和$listeners統一合並到$attrs中
props的劣勢: 父組件傳入子組件屬性,但子組件沒有接收稱為非props屬性,非props屬性默認會加到子組件標簽最外層
inheritAttrs: false的含義是不希望本組件的根元素繼承父組件的attribute,同時父組件傳過來的屬性(沒有被子組件的props接收的屬性),也不會顯示在子組件的dom元素上,但是在組件里可以通過其$attrs可以獲取到沒有使用的注冊屬性, inheritAttrs: false是不會影響 style 和 class 的綁定
$listeners的實例:
所有組件上的方法綁定子組件都可以通過$listeners接收。它可以通過 v-on="$listeners" 將所有的方法又綁定到組件相應標簽,傳入內部組件——在創建更高層次的組件時非常有用。
2、provide 和 inject的介紹與實例:
provide和inject是在2.2.0 版本新增的,主要為高階插件/組件庫提供用例。這對選項需要一起使用,以允許一個祖先組件向其所有的子孫后代注入一個依賴,不論組件的層次有多深,並在起上下游關系成立的時間里始終生效。
provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。 inject 選項應該是:一個字符串數組,或一個對象(屬性值是一個對象時,包含from和default默認值)
注意:provide和inject綁定並不是可響應的。這顯然不是設計的失誤,而是刻意的。如果要詳細了解,請參考:API — Vue.js
我們在上級組件中設置了一個provide:foo,值為beijing,它的作用就是將foo這個變量提供給它的所有下級組件。而在下級組件中通過inject注入了從上級組件中提供的foo變量,那么在下級組件中,就可以直接通過this.foo來訪問了
provide 和 inject的實例:
如果想要獲取父組件parent.vue 的多個屬性或方法時,我們應該怎么傳呢?是一個個的定義參數傳過去嗎?
parent.vue作為一個最外層的根組件,用來存儲所有需要的全局數據和狀態。我們可以把整個parent.vue實例通過provide對外提供。那么,所有的子組件、孫組件都能共享其數據,方法等。
四、VUEX狀態管理
內容太多,將在下篇單獨做介紹