情形回顾
大家都知道Vue 1.x 的版本中内置了 broadcost](https://v1-cn.vuejs.org/api/#vm-broadcast) 与 [dispatch 两个方法;用于处理事件的广播和派发;
我们简单的回顾一下这两个API的使用:
vm.$dispatch('event',[...args])
派发事件,首先在实例上触发它,然后沿着父链向上冒泡在触发一个监听器后停止,除非它返回 true
。附加参数都会传给监听器回调。
vm.$broadcost('event',[...args])
广播事件,通知给当前实例的全部后代。因为后代有多个枝杈,事件将沿着各"路径"通知。每条路径上的通知在触发一个监听器后停止,除非它返回 true
。
这无疑在很深层级的组件嵌套场景中解决了数据通讯问题,不再需要层层进行传递;
这应该是借鉴了 angular 1.x 中的事件广播与分发思想进行了实现;
虽然这两个方法使用起来很不错...
但是再vue 2.x 中都已经废弃了!!!
同时官方也解释了废弃的原因:
因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。这种事件方式确实不太好,我们也不希望在以后让开发者们太痛苦。并且$dispatch
和 $broadcast
也没有解决兄弟组件间的通信问题。
请使用更多简明清晰的组件间通信和更好的状态管理方案,如:Vuex。
不得不说这个理由确实是非常的充分。
同时官方也给出了相应的升级方案: EventBus 通过使用事件中心,允许组件自由交流,无论组件处于组件树的哪一层。由于 Vue 实例实现了一个事件分发接口,你可以通过实例化一个空的 Vue 实例来实现这个目的。
虽然在业务系统的开发过程中确实存在这个问题,他没有Vuex 这样专门的状态管理工具好用,但是他对于独立组件或者组件库的开发还是非常适用的,这是因为独立的组件结构嵌套不会太深太复杂,而且是脱离了业务的,因此也不存在官方所说的难以维护;
Element中的实现
Element ui 中依旧使用了 broadcast 与 dispatch 来解决组件的通讯问题;
下面我们来看一下他们的实现过程,相对比较简单,但依旧好用,我们添加简单的注释,便于我们理解;
源码位于 src/mixins/emitter.js
javascript
// 定义了 broadcast 方法接受 componentName, eventName, params 这三个参数
// componentName 指定了组件的名称
// eventName 指定了自定义事件的名称
// pramas 则为需要传递的数据
function broadcast(componentName, eventName, params) {
// 遍历子组件
this.$children.forEach(child => {
// 获取自定义的 componentName
var name = child.$options.componentName;
// 如果正好命中
if (name === componentName) {
// 就调用该子组建的$emit方法,绑定 this, 传递 自定义事件和数据,就结束了
child.$emit.apply(child, [eventName].concat(params));
} else {
// 如过没有命中 就 递归去调用自己,直到所有子组件遍历完或者命中
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
// 同样的参数列表...
// dispatch 于 broadcast 正好相反;是从子组件向上查找 目标组件;
dispatch(componentName, eventName, params) {
// 获取当前组件的 parent
var parent = this.$parent || this.$root;
// 获取 parent 的 componentName
var name = parent.$options.componentName;
// 循环向上查找parent
// 如果 parent 存在 并且 name 没有值 或者 name 不是目标组件
while (parent && (!name || name !== componentName)) {
// 改变 parent 为 当前parent 的parent
parent = parent.$parent;
// 获取 parent 的name
if (parent) {
name = parent.$options.componentName;
}
}
// 通过上面的循环查找最终的结果是,要么找到了目标组件,要么没有找到.
// 找到了后
if (parent) {
// parent 就调用 $emit 吧事件伴随参数抛出去
parent.$emit.apply(parent, [eventName].concat(params));
}
},
// 包装一下,改变上面定义的 broadcast this 为当前 vm,并可以通过 this.broadcast 的形式调用;
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
源码不长只有几十行.
我们可以看到它定义于mixins 目录下,以混入的形式抽离成了公共的方法,再需要使用的地方直接混入就可以了;
我们可以看到实现的方式还是非常简单,但是应用却非常频繁,比如:Select , Option ,Autocomplete , Dialog 等等组件中都使用了;
最后的最后
下面请上我们今天的主角:有请小趴菜