zoukankan      html  css  js  c++  java
  • Vue实现$dispatch和$broadcast方法

    对于父子(含跨级)传递数据的通信方式,Vue.js 并没有提供原生的 API 来支持,而是推荐使用大型数据状态管理工具 Vuex,但 Vuex 对于小型项目来说用起来真的很麻烦。

    在 Vue.js 1.x 中,提供了两个方法:$dispatch 和 $broadcast ,前者用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在组件内通过 $on (或 events,2.x 已废弃)监听到,后者相反,是由上级向下级广播事件的。

    这两种方法一旦发出事件后,任何组件都是可以接收到的,就近原则,而且会在第一次接收到后停止冒泡,除非返回 true。

    下面我们来自行实现 dispatch 和 broadcast 方法,目标是解决父子组件(含跨级)间的通信问题。

    我们要实现的 dispatch 和 broadcast 方法,将具有以下功能:

    • 在子组件调用 dispatch 方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过 $on 监听了这个事件;
    • 相反,在父组件调用 broadcast 方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过 $on 监听了这个事件。

    先来看下 emitter.js 的代码:(https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js)

    function broadcast(componentName, eventName, params) {
      this.$children.forEach(child => {
        const name = child.$options.name;
    
        if (name === componentName) {
          child.$emit.apply(child, [eventName].concat(params));
        } else {
          broadcast.apply(child, [componentName, eventName].concat([params]));
        }
      });
    }
    export default {
      methods: {
        dispatch(componentName, eventName, params) {
          let parent = this.$parent || this.$root;
          let name = parent.$options.name;
    
          while (parent && (!name || name !== componentName)) {
            parent = parent.$parent;
    
            if (parent) {
              name = parent.$options.name;
            }
          }
          if (parent) {
            parent.$emit.apply(parent, [eventName].concat(params));
          }
        },
        broadcast(componentName, eventName, params) {
          broadcast.call(this, componentName, eventName, params);
        }
      }
    };

    因为是用作 mixins 导入,所以在 methods 里定义的 dispatch 和 broadcast 方法会被混合到组件里,自然就可以用 this.dispatch 和 this.broadcast 来使用。

    这两个方法都接收了三个参数,第一个是组件的 name 值,用于向上或向下递归遍历来寻找对应的组件,第二个和第三个就是上文分析的自定义事件名称和要传递的数据。

    可以看到,在 dispatch 里,通过 while 语句,不断向上遍历更新当前组件(即上下文为当前调用该方法的组件)的父组件实例(变量 parent 即为父组件实例),直到匹配到定义的 componentName 与某个上级组件的 name 选项一致时,结束循环,并在找到的组件实例上,调用 $emit 方法来触发自定义事件 eventName。broadcast 方法与之类似,只不过是向下遍历寻找。

    来看一下具体的使用方法。有 A.vue 和 B.vue 两个组件,其中 B 是 A 的子组件,中间可能跨多级,在 A 中向 B 通信:

    在element input中的使用(https://github.com/ElemeFE/element/blob/dev/packages/input/src/input.vue

    <!-- A.vue -->
    <template>
        <button @click="handleClick">触发事件</button>
    </template>
    <script>
      import Emitter from '../mixins/emitter.js';
      
      export default {
        name: 'componentA',
        mixins: [ Emitter ],
        methods: {
          handleClick () {
            this.broadcast('componentB', 'on-message', 'Hello Vue.js');
          }
        }
      }
    </script>
    
    // B.vue
    export default {
      name: 'componentB',
      created () {
        this.$on('on-message', this.showMessage);
      },
      methods: {
        showMessage (text) {
          window.alert(text);
        }
      }
    }
    同理,如果是 B 向 A 通信,在 B 中调用 dispatch 方法,在 A 中使用 $on 监听事件即可。
  • 相关阅读:
    Linux运维工作总结教训
    java-GC
    java设计模式-原形模式
    java-桥接模式
    java-装饰者模式
    java-正则表达式
    java设计模式-建造者模式
    Python 条件与循环
    Python 集合、字典、运算符
    Python 字符串拼接、格式化输出、深浅复制
  • 原文地址:https://www.cnblogs.com/ttjm/p/12891420.html
Copyright © 2011-2022 走看看