浅析Element ui 中事件 broadcast 与 dispatch

情形回顾

大家都知道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 等等组件中都使用了;

最后的最后

下面请上我们今天的主角:有请小趴菜

相关推荐
zqx_715 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己32 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
花花鱼2 小时前
@antv/x6 导出图片下载,或者导出图片为base64由后端去处理。
vue.js
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发