本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
之前公司的项目中使用 eventBus 进行组件通信时遇到了一些数据渲染时的问题,下面对遇到的这些问题做下简单的介绍以及解决方法。我们都知道,在 Vue 中,组件通信的方式有很多,而针对那些没有父子级关系或者任何关系的组件要实现通信(数据传递)的方法就是使用 Vuex 或者 eventBus(事件总线),具体应该选择哪一种还要看实际情况。公司的项目中两种方法都有使用,而自己当时对 Vuex 的使用还不是很熟悉,所以就选择了 eventBus 来实现组件通信。然而在开发过程中还是遇到了一些问题。
首先来看下项目中要实现的效果:
大致需求是这样:左边地图组件和右边视图组件需要关联,点击左边时需要发送 机器告警(图)的数据 和 机器缺陷(图)的数据 给视图组件,而,右边视图组件中有一个公共的组件用来展示数据,用户通过点击 “机器告警” 或者 “机器缺陷” 按钮来获得对应的图示。
robotState.vue 中的 html 代码(部分):
<template>
<div>
<section class="the-wrapper">
<h2 class="title">
<span @click="switchView" :class="xxxx"></span>
<span class="switch-btn" :class="{ active: currentTab === 1 }" @click="switchTab(1)">机器告警</span>
<span class="switch-btn" :class="{ active: currentTab === 2 }" @click="switchTab(2)">机器缺陷</span>
</h2>
</section>
<!-- 机器告警图(表) -->
<section v-if="currentTab === 1" :key="0" style="xxxx">
<transition enter-active-class="animated slideInLeft">
<v-canvas-pie v-if="isPieView" ref="pie" />
<v-table v-else ref="table" />
</transition>
</section>
<!-- 机器缺陷图(表) -->
<section v-if="currentTab === 2" :key="1" style="xxxx">
<transition enter-active-class="animated slideInLeft">
<v-canvas-pie v-if="isPieView" ref="pie" />
<v-table v-else ref="table" />
</transition>
</section>
</div>
</template>
map.js 中的逻辑(部分):
getData = async city => {
// 机器告警(图)
axios.post('xxxxxxxxxxxxx',{id:city.code})
.then(res=>{
res.data.map(item=>{
Bus.$emit('AlarmInfoPie',[
{ name:"正常",value:item.normal },
{ name:"一般",value:item.commonly },
{ name:"严重",value:item.serious },
{ name:"危急",value:item.critical },
])
})
})
// 机器缺陷(图)
axios.post('mmmmmmmmmmmmmmm',{id:city.code})
.then(res=>{
res.data.map(item=>{
Bus.$emit('defectPie',[
{ name:"无缺陷",value:item.normal },
{ name:"一般缺陷",value:item.commonly },
{ name:"严重缺陷",value:item.serious },
{ name:"危急缺陷",value:item.critical },
])
})
})
}
robotState.vue 中的逻辑(部分):
export default {
components:{
VCanvasPie,
VTable
},
data(){
return {
isPieView:true,
currentTab:1,
}
},
methods: {
// 机器告警(图)
async getRobotAlarmPie(){
let temp, // 定义需要渲染的数据
const result = await axios.get('xxxxxxxxxxxxxxxx')
result.data.map(item=>{
// 数据处理(省略)
this.$refs.pie.init(temp)
})
// 渲染从 map.js 传过来的数据
Bus.$on('AlarmInfoPie',(data)=>{
temp = data
})
this.$refs.pie.init(temp)
},
// 机器缺陷(图)
async getRobotDefectPie(){
let temp, // 定义需要渲染的数据
const result = await axios.get('xxxxxxxxxxxxxxxx')
result.data.map(item=>{
// 数据处理(省略)
this.$refs.pie.init(temp)
})
// 渲染从 map.js 传过来的数据
Bus.$on('defectPie',(data)=>{
temp = data
})
this.$refs.pie.init(temp)
}
},
};
一开始数据处理逻辑就是这样,结果却是:点击地图组件的某个区域时,会进行正常渲染 “机器告警” 的数据,如果点击 “机器缺陷” 时,再点击地图组件的当前区域时却得不到对应的数据,而是渲染的是 “机器告警” 的数据,当时很懵逼,最后才知道由于 右边视图组件共用了一个数据展示的组件,当点击地图某个区域时,map.js 中的 eventBus 会有两个事件发射,robotState.vue 中对应也就有两个事件监听,从而获取数据,无论是点击 “机器缺陷” 还是 “机器告警”,后面监听的事件得到的数据都会被覆盖。找到了问题原因就很好解决,最先想到的是使用 vuex 来解决,但是需要推翻以前的代码重写,因为项目中有很多数据展示,就很麻烦;然后想的是能不能使用 eventBus 中的方法来解决,最后找到了 eventBus.$off() 方法来解决。
大致解决方法就是这样,可能解释的不是太清楚,自己可以写个案例实现下应该可以帮助理解。最后对 eventBus 的使用做一个小的补充,就是我们在项目中可能会创建多个 eventBus.js 文件,而两个或者多个组件要使用 eventBus 进行通信时一定要使用同一个 eventBus.js 文件,否则会报错,我之前就遇到过,哈哈!
总结:Vue中使用 eventBus 实现组件间的通信时,需要对 Bus.$emit()、Bus.$on()、Bus.$off() 或者其他 API 搭配使用。结合当前环境,判断是否有干扰项从而决定是否对 eventBus 进行移除。