Vue元件通訊中事件匯流排(eventBus)的使用
之前公司的專案中使用 eventBus 進行元件通訊時遇到了一些資料渲染時的問題,下面對遇到的這些問題做下簡單的介紹以及解決方法。
我們都知道,在 Vue 中,元件通訊的方式有很多,而針對那些沒有父子級關係或者任何關係的元件要實現通訊(資料傳遞)的方法就是使用 Vuex 或者 eventBus(事件匯流排),具體應該選擇哪一種還要看實際情況。公司的專案中兩種方法都有使用,而自己當時對 Vuex 的使用還不是很熟悉,所以就選擇了 eventBus 來實現元件通訊。然而在開發過程中還是遇到了一些問題。
首先來看下專案中要實現的效果:
大致需求是這樣:左邊地圖元件和右邊檢視元件需要關聯,點選左邊時需要傳送 機器告警(圖)的資料
robotState.vue 中的 html 程式碼(部分):
1 <template> 2 <div> 3 <section class="the-wrapper"> 4 <h2 class="title"> 5 <span @click="switchView" :class="xxxx"></span> 6 <span class="switch-btn" :class="{ active: currentTab === 1 }" @click="switchTab(1)">機器告警</span> 7 <span class="switch-btn" :class="{ active: currentTab === 2 }" @click="switchTab(2)">機器缺陷</span> 8 </h2> 9 </section> 10 <!-- 機器告警圖(表) --> 11 <section v-if="currentTab === 1" :key="0" style="xxxx"> 12 <transition enter-active-class="animated slideInLeft"> 13 <v-canvas-pie v-if="isPieView" ref="pie" /> 14 <v-table v-else ref="table" /> 15 </transition> 16 </section> 17 <!-- 機器缺陷圖(表) --> 18 <section v-if="currentTab === 2" :key="1" style="xxxx"> 19 <transition enter-active-class="animated slideInLeft"> 20 <v-canvas-pie v-if="isPieView" ref="pie" /> 21 <v-table v-else ref="table" /> 22 </transition> 23 </section> 24 </div> 25 </template>
map.js 中的邏輯(部分):
1 getData = async (city) => { 2 // 機器告警(圖) 3 axios.post('xxxxxxxxxxxxx',{id:city.code}) 4 .then(res=>{ 5 res.data.map(item=>{ 6 Bus.$emit('AlarmInfoPie',[ 7 { name:"正常",value:item.normal }, 8 { name:"一般",value:item.commonly }, 9 { name:"嚴重",value:item.serious }, 10 { name:"危急",value:item.critical }, 11 ]) 12 }) 13 }) 14 15 // 機器缺陷(圖) 16 axios.post('mmmmmmmmmmmmmmm',{id:city.code}) 17 .then(res=>{ 18 res.data.map(item=>{ 19 Bus.$emit('defectPie',[ 20 { name:"無缺陷",value:item.normal }, 21 { name:"一般缺陷",value:item.commonly }, 22 { name:"嚴重缺陷",value:item.serious }, 23 { name:"危急缺陷",value:item.critical }, 24 ]) 25 }) 26 }) 27 }
robotState.vue 中的邏輯(部分):
1 export default { 2 components:{ 3 VCanvasPie, 4 VTable 5 }, 6 data(){ 7 return { 8 isPieView:true, 9 currentTab:1, 10 } 11 }, 12 methods: { 13 // 機器告警(圖) 14 async getRobotAlarmPie(){ 15 let temp, // 定義需要渲染的資料 16 const result = await axios.get('xxxxxxxxxxxxxxxx') 17 result.data.map(item=>{ 18 // 資料處理(省略) 19 this.$refs.pie.init(temp) 20 }) 21 // 渲染從 map.js 傳過來的資料 22 Bus.$on('AlarmInfoPie',(data)=>{ 23 temp = data 24 }) 25 this.$refs.pie.init(temp) 26 }, 27 // 機器缺陷(圖) 28 async getRobotDefectPie(){ 29 let temp, // 定義需要渲染的資料 30 const result = await axios.get('xxxxxxxxxxxxxxxx') 31 result.data.map(item=>{ 32 // 資料處理(省略) 33 this.$refs.pie.init(temp) 34 }) 35 // 渲染從 map.js 傳過來的資料 36 Bus.$on('defectPie',(data)=>{ 37 temp = data 38 }) 39 this.$refs.pie.init(temp) 40 } 41 }, 42 };
一開始資料處理邏輯就是這樣,結果卻是:點選地圖元件的某個區域時,會進行正常渲染 “機器告警” 的資料,如果點選 “機器缺陷” 時,再點選地圖元件的當前區域時卻得不到對應的資料,而是渲染的是 “機器告警” 的資料,當時很懵逼,最後才知道由於 右邊檢視元件共用了一個數據展示的元件,當點選地圖某個區域時,map.js 中的 eventBus 會有兩個事件發射,robotState.vue 中對應也就有兩個事件監聽,從而獲取資料,無論是點選 “機器缺陷” 還是 “機器告警”,後面監聽的事件得到的資料都會被覆蓋。找到了問題原因就很好解決,最先想到的是使用 vuex 來解決,但是需要推翻以前的程式碼重寫,因為專案中有很多資料展示,就很麻煩;然後想的是能不能使用 eventBus 中的方法來解決,最後找到了 eventBus.$off() 方法來解決。