zoukankan      html  css  js  c++  java
  • Vue框架Element的事件传递broadcast和dispatch方法分析

    前言

    最近在学习饿了么的Vue前端框架Element,发现其源码中大量使用了$broadcast和$dispatch方法,而Element使用的是Vue2.0版本,众所周知在Vue 1.0升级到2.0中去除了$broadcast和$dispatch方法。

    所以在Element框架源码中将这两个函数重写实现了一遍,并采用的是minix的方式植入每个组件的代码中。

    但是Element的这两个函数虽然与官方同名,但功能却有所差异,遂有本文,简单分析一下区别于用途。

    Element的broadcast功能分析

    功能简述

    1. 在Element中broadcast(事件广播)方法需要3个参数,componentName组件名称, eventName事件名,和params数据。

    2. broadcast是寻找所有子孙组件中,组件名为componentName的组件,若找到在其组件上触发($emit)eventName的事件方法,数据为params。

    3. 假设若有3个子组件或孙子组件的组件名为指定的componentName的话,这三个组件上都会触发其指定的事件方法。

    源码对比

    vue 1.0的官方$broadcast的实现源码:

    /**

    * Recursively broadcast an event to all children instances.

    *

    * @param {String|Object} event

    * @param {...*} additional arguments

    */

    Vue.prototype.$broadcast = function (event) {

    var isSource = typeof event === 'string';

    event = isSource ? event : event.name;

    // if no child has registered for this event,

    // then there's no need to broadcast.

    if (!this._eventsCount[event]) return;

    var children = this.$children;

    var args = toArray(arguments);

    if (isSource) {

       // use object event to indicate non-source emit

       // on children

       args[0] = { nameevent, sourcethis };

    }

    //遍历所有一级子组件

    for (var i = 0, l = children.length; i < l; i++) {

       var child = children[i];

       //在每个组件上均触发指定的事件

       var shouldPropagate = child.$emit.apply(child, args);

       //若事件响应函数返回true才会向孙子组件继续广播

       if (shouldPropagate) {

         child.$broadcast.apply(child, args);

       }

    }

    return this;

    };

    Element的broadcast的实现源码:

    broadcast(componentName, eventName, params) {

    broadcast.call(this, componentName, eventName, params);

    function broadcast(componentName, eventName, params) {

       //遍历所有子组件

       this.$children.forEach(child => {

         var name = child.$options.componentName;

         //寻找符合指定名称的子组件

         if (name === componentName) {

           //在符合的自组件上触发的事件,但是不会再继续寻找符合名称的组件的子集,原因?

           child.$emit.apply(child, [eventName].concat(params));

         } else {

           //不符合继续寻找他的子集(即孙子组件)

           broadcast.apply(child, [componentName, eventName].concat([params]));

         }

       });

    }

    }

    官方的$broadcast用途的解释为:

    广播事件,通知给当前实例的全部后代。因为后代有多个枝杈,事件将沿着各“路径”通知。每条路径上的通知在触发一个监听器后停止,除非它返回 true。

    Element的broadcast与Vue1.0官方的区别对比:

    1. 官方的$broadcast的参数只有两个,event事件名和args事件数据。Element为三个,多一个组件名。

    2. 官方的$broadcast触发方式是默认只触发子代组件,不触发孙子代组件,如果子代创建了监听且返回了true,才会向孙子代组件传递事件。而Element是直接向所有子孙后代组件传递,也没有返回值判定。

    3. 最重要的区别在于用途。Element的broadcast虽然与官方的同名,但是通过分析源码可以看出Element的用途应该是 远程调用 或应取名叫childEmit,用途是调用/触发指定子孙组件的事件。而非广义上的“广播”的概念。

    最后,在Element的源码中如果找到了指定名称的组件,并在其身上触发了事件后,不会继续在其身上查询他的子组件,从用途上来讲应该是找到所有符合名称的子孙组件并触发,所以为何会这样原因不明。也许在Element的组件系统设计里面,没有自身套自身的情况?或是并不想在继续触发下级?需要再仔细分析才可了。

    Element的dispatch功能分析

    功能简述

    通过前面分析的$broadcast可以大致推导出Element中的dispatch的主要功能。

    依然是以寻找所有父级,直到找到要找的父组件,并在其身上触发指定事件。

    整体功能更类似jQuery的closest方法。

    源码对比

    vue 1.0的官方$dispatch的实现源码:

    /**

    * Recursively propagate an event up the parent chain.

    *

    * @param {String} event

    * @param {...*} additional arguments

    */

    Vue.prototype.$dispatch = function (event) {

    var shouldPropagate = this.$emit.apply(this, arguments);

    if (!shouldPropagate) return;

    var parent = this.$parent;

    var args = toArray(arguments);

    // use object event to indicate non-source emit

    // on parents

    args[0] = { nameevent, sourcethis };

    while (parent) {

       shouldPropagate = parent.$emit.apply(parent, args);

       parent = shouldPropagate ? parent.$parent : null;

    }

    return this;

    };

    Element的dispatch的实现:

    dispatch(componentName, eventName, params) {

    var parent = this.$parent || this.$root;

    var name = parent.$options.componentName;

    //寻找父级,如果父级不是符合的组件名,则循环向上查找

    while (parent && (!name || name !== componentName)) {

       parent = parent.$parent;

       if (parent) {

         name = parent.$options.componentName;

       }

    }

    //找到符合组件名称的父级后,触发其事件。整体流程类似jQuery的closest方法

    if (parent) {

       parent.$emit.apply(parent, [eventName].concat(params));

    }

    },

    最后

    通过学习Element源码中的broadcast和dispatch的实现功能,从能力角度而言,Element是将官方的 事件广播/事件派发 的功能弱化了,属于是“阉割版”的。但是从实际开发过程中的易用性角度而言,Element的做法更贴合我们日常开发过程中的需求,如父组件调用组件的方法(如全选复选框等),子组件向父组件通知变更(如表单校验等)。

  • 相关阅读:
    [转]字符串相似度算法(编辑距离算法 Levenshtein Distance)
    [转]Earth Mover's Distance (EMD)
    [转]相似度计算常用方法综述
    小和问题和逆序对问题
    递归的理解
    验证方法是否正确——对数器
    CSS的几个核心概念(复盘大纲~)
    CSS3过渡
    CSS经典布局
    CSS3边框和圆角
  • 原文地址:https://www.cnblogs.com/chern2468/p/6644108.html
Copyright © 2011-2022 走看看