浅析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 等等组件中都使用了;

最后的最后

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

相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235249 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar9 小时前
纯前端实现更新检测
开发语言·前端·javascript