// 定义 broadcast 方法,接受三个参数,分别是:组件名称、将要触发的事件名称、回调函数传递的参数 function broadcast(componentName, eventName, params) { // 依次循环当前组件的子组件 this.$children.forEach(child => { // 获取每个子组件的名字 var name = child.$options.componentName; // 判断子组件的名字是否等于传入的组件名称 if (name === componentName) { // 如果子组件的名字等于传入的组件名称就调用 $emit 触发事件 child.$emit.apply(child, [eventName].concat(params)); } else { // 如果子组件的名字不等于传入的组件名称就递归遍历调用 broadcast 孙子组件 broadcast.apply(child, [componentName, eventName].concat([params])); } }); }
差异分析
和之前说到的 $dispatch 一样,这里的 $broadcast 的两个实现版本也存在着巨大的差异:
1、接受参数:Vue 实现版本只会接受一个字符串类型的事件名称为参数,而 element-ui 实现的版本会接受三个参数,分别是:需要触发事件的组件名称、将要触发的事件名称、回调函数传递的参数;
2、实现功能:Vue 实现的 $broadcast 触发方式是默认只触发子代组件,不触发孙子代组件,如果子代创建了监听且返回了true,才会向孙子代组件传递事件。而 element-ui 实现的版本是直接向所有子孙后代组件传递,直至获取到的子组件名称等于传入的组件名称相等,才会触发当前子组件的监听事件,期间也没有返回值的判定。
11 总结
说到这里,$dispatch 和 $broadcast 的讲解就结束了。可能大家已经知道了 Vue 2.0 版本为什么会将这两个属性移除。首先我们引入的说法:
因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。这种事件方式确实不太好,我们也不希望在以后让开发者们太痛苦。并且 $dispatch 和 $broadcast 也没有解决兄弟组件间的通信问题。
这样来说 $dispatch 和 $broadcast 确实会有这样的问题。在前面的讲解中,大家也不难发现 $dispatch 主要是事件流由当前组件往父组件流动,当满足一定条件的时候就会触发当前子组件的监听事件,$broadcast 的功能是事件流由当前组件向子组件流动,当满足一定条件的时候就会触发当前子组件的监听事件。也就是说 $dispatch 和 $broadcast 主要解决了父子组件、嵌套父子组件的通信,并没有解决兄弟组件的通信问题,另一个方面这样的事件流动的方式是基于组件树结构的,当业务越来越烦杂时,这种方式会显得极其繁琐,甚至会混乱到难以维护,所以 Vue 2.0 版本移除这两个 API 是在意料之中的。
但是为什么三方 UI 库都会封装类似的这样一个组件通信的方式呢?我的猜测可能是为了解决在父子层嵌套组件中,通过 $dispatch 和 $broadcast 定向的向某个父或者子组件远程调用事件,这样就避免了通过传 props 或者使用 refs 调用组件实例方法的操作。这样说的话,$dispatch 和 $broadcast 也就其存在的价值,而并不是一无是处的,还是那句话:技术没有好与坏,只有合适不合适!