Vue.js组件精讲 组件的通信2:派发与广播——自行实现dispatch和broadcast方法

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: Vue.js 的 provide/inject API 主要用于跨级组件通信,侧重于子组件获取上级状态。但无法良好处理两种场景:父向子(跨级)传递数据和子向父(跨级)传递数据。在这种情况下,虽然Vue推荐使用Vuex,但在某些场景下,可以使用自定义的`dispatch`和`broadcast`方法。这两个方法在Vue 1.x中存在,但在2.x中被废弃。`$emit`用于触发当前组件的自定义事件,而`$on`用于监听这些事件。在Vue 2.x中,我们将自行实现`dispatch`和`broadcast`以实现类似的功能,允许父子组件(包括跨级)之间的通信,特别是当组件层级不深且无需全面状态管理时

上一讲的 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。然后有两种场景它不能很好的解决:

父组件向子组件(支持跨级)传递数据;
子组件向父组件(支持跨级)传递数据。
这种父子(含跨级)传递数据的通信方式,Vue.js 并没有提供原生的 API 来支持,而是推荐使用大型数据状态管理工具 Vuex,而我们之前已经介绍过 Vuex 的场景与在独立组件(或库)中使用的限制。本小节则介绍一种在父子组件间通信的方法 dispatch 和 broadcast。

$on 与 $emit
如果您使用过较早的 Vue.js 1.x 版本,肯定对 $dispatch 和 $broadcast 这两个内置的方法很熟悉,不过它们都在 Vue.js 2.x 里废弃了。在正式介绍主角前,我们先看看 $on 与 $emit 这两个 API,因为它们是本节内容的基础。

$emit 会在当前组件实例上触发自定义事件,并传递一些参数给监听器的回调,一般来说,都是在父级调用这个组件时,使用 @on 的方式来监听自定义事件的,比如在子组件中触发事件:

// child.vue,部分代码省略
export default {
   
  methods: {
   
    handleEmitEvent () {
   
      this.$emit('test', 'Hello Vue.js');
    }
  }
}

在父组件中监听由 child.vue 触发的自定义事件 test:

<!-- parent.vue,部分代码省略-->
<template>
  <child-component @test="handleEvent">
</template>
<script>
  export default {
   
    methods: {
   
      handleEvent (text) {
   
          console.log(text);  // Hello Vue.js
      }
    }
  }
</script>
这里看似是在父组件 parent.vue 中绑定的自定义事件 test 的处理句柄,然而事件 test 并不是在父组件上触发的,而是在子组件 child.vue 里触发的,只是通过 v-on 在父组件中监听。既然是子组件自己触发的,那它自己也可以监听到,这就要使用 $on 来监听实例上的事件,换言之,组件使用 $emit 在自己实例上触发事件,并用 $on 监听它。

听起来这种神(sāo)操作有点多此一举,我们不妨先来看个示例:

```js
<template>
  <div>
    <button @click="handleEmitEvent">触发自定义事件</button>
  </div>
</template>
<script>
  export default {
   
    methods: {
   
      handleEmitEvent () {
   
        // 在当前组件上触发自定义事件 test,并传值
        this.$emit('test', 'Hello Vue.js')
      }
    },
    mounted () {
   
      // 监听自定义事件 test
      this.$on('test', (text) => {
   
        window.alert(text);
      });
    }
  }
</script>

$on 监听了自己触发的自定义事件 test,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

仅上面的示例,的确是多此一举的,因为大可在 handleEmitEvent 里直接写 window.alert(text),没必要绕一圈。

之所以多此一举,是因为 handleEmitEvent 是当前组件内的 调用的,如果这个方法不是它自己调用,而是其它组件调用的,那这个用法就大有可为了。

了解了 $on 和 $emit 的用法后,我们再来看两个“过时的” API。

Vue.js 1.x 的 $dispatch 与 $broadcast
虽然 Vue.js 1.x 已经成为过去时,但为了充分理解本节通信方法的使用场景,还是有必要来了解一点它的历史。

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

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

这两个方法虽然看起来很好用,但是在 Vue.js 2.x 中都废弃了,官方给出的解释是:

因为基于组件树结构的事件流方式有时让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。

虽然在业务开发中,它没有 Vuex 这样专门管理状态的插件清晰好用,但对独立组件(库)的开发,绝对是福音。因为独立组件一般层级并不会很复杂,并且剥离了业务,不会变的难以维护。

知道了 $dispatch 和 $broadcast 的前世今生,接下来我们就在 Vue.js 2.x 中自行实现这两个方法。

自行实现 dispatch 和 broadcast 方法
自行实现的 dispatch 和 broadcast 方法,不能保证跟 Vue.js 1.x 的 $dispatch 和 $broadcast 具有完全相同的体验,但基本功能是一样的,都是解决父子组件(含跨级)间的通信问题。

通过目前已知的信息,我们要实现的 dispatch 和 broadcast 方法,将具有以下功能:

在子组件调用 dispatch 方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过 $on监听了这个事件;
相反,在父组件调用 broadcast 方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过 $on监听了这个事件。
实现这对方法的关键点在于,如何正确地向上或向下找到对应的组件实例,并在它上面触发方法。在设计一个新功能(features)时,可以先确定这个功能的 API 是什么,也就是说方法名、参数、使用样例,确定好 API,再来写具体的代码。

因为 Vue.js 内置的方法,才是以 $ 开头的,比如 $nextTick、$emit 等,为了避免不必要的冲突并遵循规范,这里的 dispatch 和 broadcast 方法名前不加 $。并且该方法可能在很多组件中都会使用,复用起见,我们封装在混合(mixins)里。

目录
相关文章
|
1天前
|
JavaScript
Vue3的 组件事件
Vue3的 组件事件
10 0
|
1天前
|
JavaScript
Vue自定义指令的三个方法
Vue自定义指令的三个方法
7 0
|
1天前
|
存储 JavaScript
vue3组件之间传值通讯
vue3组件之间传值通讯
6 0
|
1天前
|
JavaScript 前端开发
JavaScript 循环方法详解
JavaScript 循环方法详解
15 1
|
1天前
|
JavaScript 前端开发
JavaScript数字方法详解
JavaScript数字方法详解
13 0
|
1天前
|
存储 JavaScript 前端开发
JavaScript对象方法详解
JavaScript对象方法详解
9 1
|
1天前
|
存储 JavaScript 前端开发
JavaScript字符串方法详解
JavaScript字符串方法详解
12 0
|
1天前
|
资源调度 JavaScript 前端开发
vue3怎么调用vant中的icon组件
vue3怎么调用vant中的icon组件
15 4
|
2天前
|
JavaScript 前端开发
javascript数组排序的方法
javascript数组排序的方法
|
2天前
|
JavaScript 前端开发 安全
javascript操作DOM的方法
javascript操作DOM的方法