vue中央事件总线问题

问题

业务场景,需要在兄弟组件之间传值,选择了中央事件总线解决办法,如下

1
2
3
4
5
6
7
8
//  bus.js
import Vue from 'vue';

const bus = new Vue({});

export {
bus
}
1
2
3
//  brother-a.vue
import { bus } from 'bus.js'
bus.$emit('action', val)
1
2
3
4
5
//  brother-b.vue
import { bus } from 'bus.js'
bus.$on('action', msg => {
// ...do something
})

但是实际使用过程中发现问题 brother-b.vue 中没有监听到值

原因

因为 b 组件是动态生成的,此时没有 $on,无法监听到事件,也就是说

在$emit时,必须已经$on,否则将无法监听到事件,对组件是有一定的同时存在的要求的

为什么会这样呢?

中央事件总线的原理 是以一个Vue实例为桥梁,通过调用其$emit, $on事件实现组件的通信

查看Vue $emit源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//  vue 2.6.2
Vue.prototype.$emit = function (event) {
var vm = this;
{
var lowerCaseEvent = event.toLowerCase();
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
"Event \"" + lowerCaseEvent + "\" is emitted in component " +
(formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
"Note that HTML attributes are case-insensitive and you cannot use " +
"v-on to listen to camelCase events when using in-DOM templates. " +
"You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
);
}
}
// 这一步会获取 所有的监听函数,并执行
var cbs = vm._events[event];
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs;
var args = toArray(arguments, 1);
var info = "event handler for \"" + event + "\"";
for (var i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info);
}
}
return vm
};
}

在 emit 的时候会获取回调函数并执行,此时如果没有on监听函数,当新的组件生成on函数也就不被监听了也不会执行。

解决方法

  1. 可以使用Vuex
  2. 将 a 组件的值this.$emit()传到了 父组件 , 在从父组件通过props传值到 b 组件