Vue3中如何自定义消息总线

前言

Vue 开发中,组件之间的通信是一个常见的需求,无论是父组件向子组件传递数据,还是子组件向父组件传递数据,甚至是兄弟组件之间的数据交换。这些通信需求在构建复杂的 Vue 应用时尤为关键。

Vue 提供了多种组件通信的方式,如 props 用于父组件向子组件传递数据,emit 用于子组件触发事件并传递数据给父组件,vuex 适用于状态管理场景,而 provide/inject 则提供了依赖注入的方式。在 Vue 2 中,还有 eventBus$attrs/$listeners 以及 $parent/children 等方法来辅助组件间的通信。

然而,随着 Vue 3 的发布,一些在 Vue 2 中常用的通信方式在 Vue 3 中可能不再适用或有所变化。为了应对这种变化,我们可以选择在 Vue 3 应用中实现自定义的“事件总线”机制。这种机制通常是将一个事件中心(或称为事件总线)挂载到Vue的全局对象上,从而使得任何组件都可以方便地通过事件总线来发布或监听事件。

通过使用这样的自定义事件总线,开发者可以在 Vue 3 应用中实现灵活的组件间通信,无论这些组件之间的层级关系如何,都能轻松地实现数据和事件的传递。

发布-订阅模式

发布-订阅模式(Publish-Subscribe Pattern)是一种在软件设计中常见的模式,它允许消息发送者和接收者之间通过事件进行通信,而不必直接相互依赖。这种模式的主要思想是通过一个被称为“消息中心”或“事件总线”的实体来协调消息的发布和订阅。

一个完整的发布-订阅模式通常包含以下几个部分:

  • 发布者(Publisher):负责向消息中心发布事件或消息的对象。发布者通常不关心谁订阅了这些事件,只负责在特定情况下触发它们。
  • 订阅者(Subscriber):对特定事件感兴趣的对象,它们会向消息中心订阅这些事件。当发布者发布一个事件时,所有订阅了该事件的订阅者都会收到通知。
  • 消息中心(Event Bus/Message Center):负责管理事件的发布、订阅和通知的对象。它存储了事件和订阅者之间的关系,并在事件被发布时,将事件通知给所有订阅了该事件的订阅者。
    在这里插入图片描述

发布-订阅模式的优点包括:

  1. 解耦性:发布者和订阅者之间不存在直接的依赖关系,这使得它们可以独立地变化,而不需要修改对方。这种解耦性提高了代码的可维护性和可扩展性。
  2. 可扩展性:可以轻松地添加新的发布者和订阅者,而无需修改现有的代码。这使得系统能够灵活地适应不断变化的需求。
  3. 灵活性:支持多个订阅者同时订阅同一个事件,并且可以根据需要定制事件的处理方式。这种灵活性使得系统能够应对各种复杂场景。
  4. 时间解耦:发布者可以在任何时刻发布事件,而订阅者则可以在自己方便的时候处理这些事件。这种时间解耦性使得系统更加灵活和高效。

综上所述,发布-订阅模式提供了一种强大而灵活的方式来处理组件之间的通信和协作,使得系统更加健壮、可维护和可扩展。在Vue.js 等现代前端框架中,发布-订阅模式被广泛应用于组件之间的通信和状态管理。

实现发布-订阅模式

在深入探讨发布-订阅模式时,其核心机制在于一个精心构建的事件中心。这个事件中心不仅作为消息的中转站,还承载着存储事件和订阅者之间关系的重要职责。

事件中心

定义一个 EventBus 类,用于存储事件和订阅者关系,代码如下:

interface EventType {
  readonly callback: Function
  readonly once: boolean
}

type EventsType = Record<string, EventType[]>
class EventEmitter {
  private events: EventsType = {};
}

events 是一个对象,其中每个键都是事件名称,值是一个由 EventType 对象组成的数组,EventType 对象中包含是否只订阅一次标志位和回调函数。

发布事件

定义一个 emit 方法,用于发布事件,代码如下:

type EventArgs = Record<string, number | string | object | boolean>

interface EventType {
  readonly callback: Function
  readonly once: boolean
}

type EventsType = Record<string, EventType[]>

class EventEmitter {
  private events: EventsType = {}
  
  emit(eventName: string, eventArgs: EventArgs) {
    eventName?.split(',').forEach((eventKey: string) => {
      eventKey = eventKey.trim()
      const events = this.events[eventKey] || []
      let { length } = events

      for (let i = 0; i < length; i++) {
        const { callback, once } = events[i]
        if (once) {
          events.splice(i, 1)
          if (length === 0) {
            delete this.events[eventKey]
          }
          length--
          i--
        }

        callback.apply(this, [eventArgs])
      }
    })
  }
}

emit 方法接收两个参数,第一个参数 eventName 为事件名称,第二个参数 eventArgs 为事件参数。其中 eventName 参数可以是一个以逗号分隔的字符串,表示同时发布多个事件。核心逻辑便是遍历 events 对象,找到对应的事件名称,然后遍历事件名称对应的事件数组,依次通过调用 apply 方法,执行回调函数。

订阅事件

定义一个 on 方法,用于订阅事件,代码如下:

class EventEmitter {
  private events: EventsType = {}

  on(eventName: string, callback: CallbackType, once?: boolean) {
    eventName?.split(',').forEach((eventKey: string) => {
      eventKey = eventKey.trim()
      if (!this.events[eventKey]) {
        this.events[eventKey] = [];
      } else {
        this.events[eventKey].push({
          callback,
          once: !!once
        })
      }
    })

    return this
  }
}

on 方法接收三个参数,第一个参数 eventName 为事件名称,第二个参数 callback 为回调函数,第三个参数 once 表示是否只订阅一次。核心逻辑是遍历 eventName 参数 split 之后的数组对象(允许同时监听多个事件,多个事件之间以逗号分隔),将事件名称拆分成数组,然后遍历数组,将回调函数存入 events 对象中。

取消订阅事件

定义一个 off 方法,用于取消订阅事件,代码如下:

class EventEmitter {
  private events: EventsType = {}

  off(eventName: string, callback?: CallbackType) {
    // 如果不传callback,就清除所有事件
    if (!eventName) {
      this.events = {}
    }
    eventName?.split(',').forEach((eventKey: string) => {
      if(!callback) {
        delete this.events[eventKey]
      }
      const events = this.events[eventKey] || []
      let { length } = events
      for (let i = 0; i < length; i++) {
        if (events[i].callback === callback) {
          events.splice(i, 1)
          length --
          i --
        }
      }

      if (events.length === 0) {
        delete this.events[eventKey]
      }
    })

    return this
  }
}

off 方法接收两个参数,第一个参数 eventName 为事件名称,第二个参数 callback 为回调函数,核心逻辑是遍历 eventName 参数 split 之后的数组对象(允许同时取消订阅多个事件,多个事件之间以逗号分隔),将事件名称拆分成数组,然后遍历数组,将回调函数从数组中删除。

只订阅一次

定义一个 once 方法,用于只订阅一次,代码如下:

class EventEmitter {
  private events: EventsType = {}

  once(eventName: string, callback: CallbackType) {
    return this.on(eventName, callback, true)
  }
}

once 方法接收两个参数,第一个参数 eventName 为事件名称,第二个参数 callback 为回调函数。once 方法内部调用 on 方法,并将第三个参数设置为 true,表示只订阅一次。

如何在 Vue 中使用

当我们想要在 Vue 应用中使用发布-订阅模式时,通常会引入一个全局的事件总线 (Event Bus) 来作为通信的中心。这样,无论组件之间有着怎样的层级关系,它们都可以轻松地通过事件总线进行通信。

为了在 Vue 应用中实现这一功能,我们需要在应用的入口文件(通常是 main.tsmain.js,取决于你的项目配置和所使用的 TypeScriptJavaScript)中引入并实例化事件总线。然后,我们可以利用 Vueprovide 方法将事件总线注册为全局对象,使得在 Vue 应用的任何组件中都能通过 inject 来访问它。

main.ts 中编写代码如下:

import { createApp } from 'vue'
import { EventEmitter } from '@qftjs/tiny-editor-core'
import App from './App.vue'

const app = createApp(App)

const bus = new EventEmitter()
app.provide('$bus', bus)

app.mount('#app')

Vue 组件中通过 inject 方法注入 $bus 对象,然后就可以使用 $bus 对象进行事件订阅和事件发布。具体代码如下:

<script setup lang="ts">
import { inject } from 'vue'

const $bus = inject('$bus')

$bus.emit('dragStart')
</script>

更多精彩文章,欢迎关注我的公众号:前端架构师笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/649493.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统安装AMH服务器管理面板并实现远程访问管理维护

目录 前言 1. Linux 安装AMH 面板 2. 本地访问AMH 面板 3. Linux安装Cpolar 4. 配置AMH面板公网地址 5. 远程访问AMH面板 6. 固定AMH面板公网地址 1. 部署Docker Registry 2. 本地测试推送镜像 3. Linux 安装cpolar 4. 配置Docker Registry公网访问地址 5. 公网远程…

如何被谷歌收录?

最简单的方法就是提交网站给谷歌&#xff0c;但这种方法可操作空间不大&#xff0c;一天一般也就只有十条左右的链接可以提交&#xff0c;对于一些大网站来说&#xff0c;这种方法显然不适用&#xff0c;这时候GPC爬虫池的好处就体现了&#xff0c;GPC爬虫池对希望提升Google搜…

re:记录下正则的使用方法

1、match pattern r(\d{4})[-\/](\d{1,2})[-\/](\d{1,2}) match re.search(pattern, text) if match:year, month, day match.groups()

SolidWorks教育版 学生使用的优势

在工程技术领域的学习中&#xff0c;计算机辅助设计软件&#xff08;CAD&#xff09;如SolidWorks已经成为学生掌握专业知识和技能的必要工具。SolidWorks教育版作为专为教育机构和学生设计的版本&#xff0c;不仅提供了与商业版相同的强大功能&#xff0c;还为学生带来了诸多独…

TypeScript 语言在不改变算法复杂度前提下,细节上性能优化,运行时性能提升效果明显吗?

有经验的专家写的代码&#xff0c;和无经验的新手写的代码&#xff0c;在运行时性能上大概会有多少差异&#xff1f; 个人感觉&#xff0c;常规业务逻辑代码通常可以差 1 倍&#xff1b;如果算上框架的影响&#xff0c;可以差 2~4 倍。 仅考虑业务代码的话&#xff0c;新手容易…

科普:水冷负载的工作原理

水冷负载是一种利用水作为冷却介质&#xff0c;将电子设备产生的热量传递到外部环境的散热方式。它广泛应用于各种电子设备&#xff0c;如服务器、数据中心、电力设备等&#xff0c;以提高设备的运行效率和稳定性。本文将对水冷负载的工作原理进行简要科普。 水冷负载的工作原理…

我被恐吓了,对方扬言要压测我的网站

大家好我是聪&#xff0c;昨天真是水逆&#xff0c;在技术群里交流问题&#xff0c;竟然被人身攻击了&#xff01;骂的话太难听具体就不加讨论了&#xff0c;人身攻击我可以接受&#xff0c;我接受不了他竟然说要刷我接口&#xff01;&#xff01;&#xff01;&#xff01;这下…

C#多维数组不同读取方式的性能差异

背景 近来在优化一个图像显示程序&#xff0c;图像数据存储于一个3维数组data[x,y,z]中&#xff0c;三维数组为一张张图片数据的叠加而来&#xff0c;其中x为图片的张数&#xff0c;y为图片行&#xff0c;Z为图片的列&#xff0c;也就是说这个三维数组存储的为一系列图片的数据…

记录下所遇到远程桌面连接方法winSCP跟mstsc

之前公司使用过连接远程桌面&#xff0c;今天又遇到要使用远程桌面问题&#xff0c;来记录下。 之前公司使用的是winR 然后回车弹出 后面按照用户名密码就能登陆了 今天后台给了我一张图片准备接着用这个方法&#xff0c;后台就说这个东西要下载winSCP 后台发给我图片 然后去…

mfc140u.dll丢失的解决方法有哪些?怎么全面修复mfc140u.dll文件

mfc140u.dll丢失其实相对来说不太常见到&#xff0c;因为这个文件一般是不丢失的&#xff0c;不过既然有人遇到这种问题&#xff0c;那么小编一定满足各位&#xff0c;给大家详细的唠叨一下mfc140u.dll丢失的各种解决方法&#xff0c;教大家以最快最有效率的方法去解决mfc140u.…

python文件IO基础知识

目录 1.open函数打开文件 2.文件对象读写数据和关闭 3.文本文件和二进制文件的区别 4.编码和解码 读写文本文件时 读写二进制文件时 5.文件指针位置 6.文件缓存区与flush()方法 1.open函数打开文件 使用 open 函数创建一个文件对象&#xff0c;read 方法来读取数据&…

CentOS7 部署单机版 ElasticSearch + Logstash

一、部署ElasticSearch Elasticsearch部署参考下面文章&#xff1a; CentOS7 部署单机版 elasticsearch-CSDN博客文章浏览阅读285次&#xff0c;点赞6次&#xff0c;收藏3次。ElasticSearch&#xff0c;用于检索、聚合分析和大数据存储https://blog.csdn.net/weixin_44295677…

定个小目标之每天刷LeetCode热题(1)

有两种解决方法&#xff1a; 第一种&#xff1a;利用哈希集合不重复的特性&#xff0c;代码展示如下 public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {Set<ListNode> listNode new HashSet<ListNode>();ListNode…

一招搞定!家里灰尘多?教你如何轻松清理,推荐必备神器

在现代生活中&#xff0c;灰尘无处不在&#xff0c;特别是在大城市&#xff0c;空气中的污染物更多&#xff0c;导致家里的灰尘积聚速度加快。保持家居环境的干净和整洁不仅能提升生活质量&#xff0c;还能保护我们的健康。作为一名家电博主将为你提供详细的家里灰尘清理方法&a…

EtherCAT运动控制器在UVW对位平台中的应用

ZMC406硬件介绍 ZMC406是正运动推出的一款多轴高性能EtherCAT总线运动控制器&#xff0c;具有EtherCAT、EtherNET、RS232、CAN和U盘等通讯接口&#xff0c;ZMC系列运动控制器可应用于各种需要脱机或联机运行的场合。 ZMC406支持6轴运动控制&#xff0c;最多可扩展至32轴&#…

Taro React组件开发 —— RuiVerifySlider 行为验证码之滑动拼图

1. 效果预览 2. 使用场景 账号登录,比如验证码发送,防止无限调用发送接口,所以在发送之前,需要行为验证! 3. 插件选择 AJ-Captcha行为验证码文档AJ-Captcha行为验证码代码仓库为什么要选用【AJ-Captcha行为验证码】呢?因为我们管理后台使用的是 pigx ,它在后端采用的是【…

双盲插技术引领行业变革:USB-C便携显示器新篇章

USB TYPE-C接口显示器技术解析&#xff1a;LDR6282芯片与双盲插全功能方案 随着USB TYPE-C接口技术的普及和USB4标准的推出&#xff0c;传统HDMI和DisplayPort接口的地位正逐渐受到挑战。USB TYPE-C接口以其小巧、高速、多功能的特性&#xff0c;正逐步成为显示器和电视机接口…

IDEC和泉触摸屏维修显示屏HG1G-4VT22TF-S

IDEC触摸屏维修故障有&#xff1a;黑屏、花屏、按触摸屏无反应或反应慢、内容错乱、进不了系统界面、无背光、背光暗、有背光无字符、不能通信、按键无反应等。 和泉IDEC触摸屏维修常见型号&#xff1a;HG2A-SS22CF、HG2F-SS22VF 、HG2F-SS52VF、HG3F-FT22TF-B、HG3F-FT22TF-W、…

探索数据结构:单链表的实践和应用

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;渐入佳境之数据结构与算法 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 一、前言 前面我们学习了数据结构中的顺序表&…

前端JS必用工具【js-tool-big-box】学习,获取全球重点城市时间

我们去住一些旅馆的时候&#xff0c;或者一些国际性网站&#xff0c;经常可以看见他们的钟表会展示一些国家地区的时间&#xff0c;这个就是很常用的功能。但如果不常接触这个功能的开发网站呢&#xff0c;大家就看自己电脑右下角的时间展示&#xff0c;就是自己当前的具体时间…