发布订阅模式以及mitt源码实现

发布订阅模式以及mitt源码实现

前言:我为什么要写他?
场景1: 我在写一个组件,但是层层传递之后,全是属性/事件的传递。中间有很多缘由,vuex 又不适合,最后选择了eventBus,但是vue3 已经不再提供eventBus,可以用mitt库 来做EventBus(后面会实现一个mitt)

场景2: 在实现promise的时候,遇到了setTimeOut的处理,但是promise.then 不会一直等待setTimeOut,所以一直是PENDING转状态,那怎么让setTimeOut之后,再来改变状态呢,此时就需要一个事件队列,来存储这些PENDING的事件,等到resolve之后,如果还是PENDING,则以此执行事件队列里的事件,这也是一个发布订阅《EventBus》

梳理一下名词

发布订阅模式

  • 发布-订阅者模式(Publish-Subscribe Pattern)是一种软件设计模式,用于实现对象间的一对多通信。在该模式中,存在两种角色:发布者(Publisher)和订阅者(Subscriber)。

  • 发布者(Publisher) :负责发布(或广播)消息,通常不知道谁会接收这些消息。

  • 订阅者(Subscriber) :订阅(或监听)发布者的消息,并在发布者发布消息时收到通知。

这种模式的实现通常依赖于一个中介者(也称为事件总线或消息队列),发布者将消息发送到中介者,中介者负责将消息传递给所有已经订阅该消息类型的订阅者。

在前端开发中,发布-订阅者模式常用于解耦组件之间的通信,特别是在涉及到跨组件通信或全局状态管理时。一些流行的 JavaScript 库和框架(如 Vue.js 和 Redux)都提供了发布-订阅者模式的实现或支持。

总的来说,发布-订阅者模式提供了一种灵活的方式来实现组件间的通信,降低了它们之间的耦合度,使得系统更易于维护和扩展。

EventBus(事件总线:采用发布订阅模式的一个工具,来处理组件通信

  • 在Vue.js中,EventBus是一种简单而强大的实现发布-订阅者模式的工具。它实际上是一个Vue实例,可以用于在组件之间进行通信。但是vue3取消了全局事件总线,由于EventBus是一个全局实例,所以在使用时要小心不要造成命名冲突或滥用,以免导致代码维护和调试困难,安全性低。。,但是某些场景,我们确实需要事件总线,怎么办呢?
mitt / tiny-emitter ?

Mitt 和 tiny-emitter 都是 JavaScript 事件发射器库,可以用于在 JavaScript 中进行事件发布和订阅。但是,它们有一些明显的区别:

  • 体积:Mitt 比 tiny-emitter 更小,压缩后仅有 200 字节。tiny-emitter 的体积略大,压缩后约为 2.2 KB。
  • 支持的浏览器版本:Mitt 支持 IE9+,而 tiny-emitter 支持 IE6+。

尽管它们有一些区别,Mitt 和 tiny-emitter 都是功能强大且易于使用的事件发射器库。选择哪个库取决于你的应用程序的特定需求和偏好。

mitt

mitt 的 Usage

import mitt from 'mitt'const emitter = mitt()// listen to an event
emitter.on('foo', e => console.log('foo', e) )// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )// fire an event
emitter.emit('foo', { a: 'b' })// clearing all events
emitter.all.clear()// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

通过Usage,我们来看看mitt的大致实现步骤

  1. 首先使用mitt,是通过函数调用的形式(**const emitter = mitt()**),再通过(.)语法来调用方法,说明mitt 是一个函数内部是返回了 on, emit, off, once 方法
  2. 方法参数 订阅on 订阅《key,handler》发布: emit《key,params》取消订阅: off<key, handler?>订阅一次: once
  3. 下面就是 key 和handler 怎么做对应关系? 怎么知道我的key对应的订阅handler,发布的handler,取消的handler ?
  4. 使用队列/Map 来对数据做存储,保证顺序和对应关系
  • 订阅: 没有则创建一个数组集合,有则添加至对应的key下
  • 发布: 遍历key,执行对应的handler
  • 取消订阅: 遍历key,如果有handler,则删除对应的handler。 没有handler 则删除key 对应的所有handler
  • once: 注册一个函数,先执行,再取消 (其实mitt 内部并没有实现)

mitt源码实现

function mitt() {let all = new Map();/*** 订阅方法* @param {*} key  传递的事件key* @param {*} handler  事件处理函数*/const on = (key, handler) => {// 如果没有key 对应的数组,则创建一个if (!all.get(key)) {all.set(key, []);}// 将handler 存入对应的key 对应的数组中all.get(key).push(handler);};/*** 发布方法* @param {*} key 发布给订阅者的key* @param {*} params  发布者的参数,告诉订阅者我发布了什么内容。*/const emit = (key, params) => {// 判断是否有对应的keylet handlers = all.get(key);// if (!handlers) return; // 这不能return 啊,如果是* 后面还要做判断滴// 如果存在对应的keyif (handlers) {// 获取对应的key 对应的数组// 遍历数组,执行对应的事件处理函数handlers.slice().map((handler) => handler(params));}// 如果没有查到监听事件,看是否监听了所有事件handlers = all.get('*');// 如果是订阅了所有事件,则以此执行订阅的* 事件if (handlers) {handlers.slice().map((handler) => handler(params));}};/** 移除事件方法* @param {*} key * @param {*} handler?  如果传入handler,则移除制定的handler。如果没有传入handler,则移除key下所有的handler*/const off = (key, handler) => {let handlers = all.get(key);if (handlers) {// handler ? handlers.splice(handlers.indexOf(handler), 1) : handlers.length =0;handler ? handlers.splice(handlers.indexOf(handler), 1) : all.set(key, []);}console.log('all',all)};/*** 只执行一次方法:内部也是调用on,执行完,取消掉* @param {*} type * @param {*} handler */const once = (type, handler) => {const onceHandler = (params)=>{handler(params);off(type, handler);}on(type,onceHandler)};return {on,off,emit,once,};
}
const emitter = mitt();
emitter.on("foo", (params) => {console.log("foo1!",params);
});
emitter.once("foo", (params) => {console.log("foo2!",params);
});
// emitter.once("foo", (params) => {
//   console.log("foo!",params);
// });
emitter.on("*", (params) => {console.log("*!",params);
})
// emitter.off('foo')
emitter.emit("foo", "w");
console.log("emitter", emitter);

问题思考

  • 整体流程是什么?

先订阅,看有没有对应的key,没有就创建一个消息队列(前端就是创建一个数组集合),来存放订阅的事件。等到发布者根据key,发布事件,会传递参数给订阅者,订阅者此时会收到发布者发来的消息。

  • 怎么取消订阅?

先判断有没有对应的key, 并且 有要移除的函数。则从对应的key 对应的数组中,移除对应的订阅的事件。如果没有要移除的函数,则根据对应key, 将所有的事件队列清空

  • 怎么只执行一次?

先订阅,订阅的时候,创建一个新的函数,这个函数会执行一次,然后取消订阅。

  • 怎么发布所有事件?

先判断有没有订阅所有事件,如果有,则遍历所有事件,执行对应的事件处理函数。
事件订阅优缺点?

优点:

  • 简化通信: 不用考虑组件层级,组件间可以直接通信,不用一个向上/下 传递 属性/事件。是一个全局性质的模式

缺点:

  • 容易滥用: 其实组件之间本身是需要关系的大量使用事件总线使得组件之间的关系变得模糊

  • 难以跟踪:事件总线模式可能会导致事件流变得难以跟踪和理解,特别是在大型应用程序中。由于事件的传递是异步的,因此可能难以确定事件的来源和处理过程

参考链接地址

mitt: https://github.com/developit/mitt/blob/main/src/index.ts

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

【运输层】TCP 的流量控制和拥塞控制

目录 1、流量控制 2、TCP 的拥塞控制 &#xff08;1&#xff09;拥塞控制的原理 &#xff08;2&#xff09;拥塞控制的具体方法 1、流量控制 一般说来&#xff0c;我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#x…

milvus服务安装bash脚本指令理解

下拉镜像&#xff1a;docker pull milvusdb/milvus:v2.4.0-rc.1下载文件&#xff1a;https://hub.yzuu.cf/milvus-io/milvus/blob/master/scripts/standalone_embed.sh安装启动&#xff1a;bash standalone_embed.sh start详细解释下这段代码&#xff1a;wait_for_milvus_runni…

伪代码——基础语法入门

1、简介 伪代码是一种用来描述算法或程序逻辑的抽象化编码方式&#xff0c;它不依赖于任何特定的编程语言语法&#xff0c;而是使用类似自然语言的形式来描述算法步骤。通常用于算法设计、教学和沟通&#xff0c;伪代码可以更直观地表达问题的解决方案&#xff0c;而不必受限于…

Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机

Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机 文章目录 Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机1.安装virtualbox2.下载Window.iso文件并载入3.问题解决3.1 Kernel driver not installed (rc-1908)3.2 VT-x is disabled in the BIOS for all CPU modes 4.安装Wi…

【python】python 模块学习之--Fabric

基础一&#xff1a; #!/usr/bin/env pythonfrom fabric.api import *env.userrootenv.hosts[218.78.186.162,125.208.12.56]env.passwords{ root218.78.186.162:22:XXX,root125.208.12.56:22:XXXX0}runs_once ####runs_once代表只执行一次def local_tas…

在开源框架使用自有数据集的方法-以增量学习工具箱PyCIL为例

回答多位朋友提出的&#xff0c;如何在开源框架使用自有数据集。思路是理解开源代码的设计方法&#xff0c;根据其设计方法增加相应的代码。 具体方法如下&#xff1a; 1.查看开源代码提供者的说明 https://github.com/G-U-N/PyCIL#datasets&#xff0c;这里提供了入手的起点…

带你实现一个github注册页面的星空顶

带你实现一个github注册页面的星空顶 github的注册页面可以说是非常的好看&#xff0c;如果没有看过的可以看下面的图片&#xff1a; 那么要如何实现下面的这个效果呢&#xff1f; 首先我们研究一下他的这个官网 首先我看到的后面的这个背景&#xff0c;是不是一个纯色的背景…

Linux安装Docker完整教程及配置阿里云镜像源

官网文档地址 安装方法 1、查看服务器内核版本 Docker要求CentOS系统的内核版本高于3.10 uname -r #通过 uname -r 命令查看你当前的内核版本2、首先卸载已安装的Docker&#xff08;如果有&#xff09; 2.1 确保yum包更新到最新 yum update2.2 清除原有的docker&#xff0c…

02_Fixture定位,Caliper卡尺工具,几何学工具

Fixture定位工具 需求: 测量工件的尺寸 使用Caliper(卡尺)工具 这个时候需要借助Fixture工具 VisionPro中的图像空间 “” 图像的当前空间&#xff0c;即CogImage中的“SelectedSpaceName”表示的名字空间 “#” 像素空间&#xff0c;即坐标原点为图片左上角的坐标空间&am…

TCP/IP协议—MQTT

TCP/IP协议—MQTT MQTT协议MQTT协议特点MQTT通信流程MQTT协议概念 MQTT报文固定报头可变报头有效载荷 MQTT协议 消息队列遥测传输&#xff08;Message Queuing Telemetry Transport&#xff0c;MQTT&#xff09;是一个基于客户端-服务器的消息发布/订阅传输协议。它的设计思想…

windows上安装make

下载地址 https://sourceforge.net/projects/gnuwin32/ 点击框中的下载&#xff0c;下载后安装。把安装路径添加到环境变量 PATH 中. 打开cmd&#xff0c;验证是否生效 安装包下载地址&#xff1a; https://download.csdn.net/download/qq_36314864/89163210

python读取DBF数据

DBF文件通常是由数据库软件&#xff08;如FoxPro或dBASE&#xff09;创建的数据库文件。Python中并没有直接读取DBF文件的内置库&#xff0c;但你可以使用第三方库如dbfread来读取DBF文件。 首先&#xff0c;你需要安装dbfread库。你可以使用pip来安装&#xff1a; pip insta…

【人工智能书籍分享】从ChatGPT到AIGC:人工智能重塑千行百业

今天又来给大家推荐一本人工智能方面的书籍<从ChatGPT到AIGC&#xff1a;人工智能重塑千行百业>。本书介绍了ChatGPT的前世今生&#xff0c;重点聚焦普通人如何使用ChatGPT获得工作和生活效率的提升&#xff0c;各行各业如何通过ChatGPT来改变自己的赛道状态。 使用Chat…

vue中params和query的区别

用法&#xff1a;query要用path来引入&#xff0c;params要用name来引入&#xff0c;接收参数都是类似的&#xff0c;分别是 this.$route.query.name 和 this.$route.params.name 。 url地址显示&#xff1a;query更加类似于ajax中get传参&#xff0c;params则类似于post&…

免费SSL证书的不香吗?四步轻松搞定

随着技术的发展和普及&#xff0c;现在有许多机构提供免费的SSL证书&#xff0c;使得即便是拥有有限预算的网站也能够享受到基本的加密服务。几乎所有的公有云服务商都提供了免费版ssl&#xff0c;如阿里云曾推出免费型DV SSL证书&#xff0c;腾讯云和百度云也有类似的免费SSL证…

平衡车设计——硬件篇

在本文开始之前我忍不住想吐槽一个事情&#xff0c;就在前两天晚上&#xff0c;我满意地装完平衡车&#xff0c;给他取了个名字叫瓦力&#xff08;没错&#xff0c;就是机器人总动员里的瓦力&#xff09;&#xff0c;他长这个样子。 把他放到桌子上放了一夜&#xff0c;第二天早…

ffmpeg buffer管理

除了内存管理之外&#xff0c;数据的前后级流转也涉及到buffer管理. 个人觉得ffmpeg里面的buffer管理实现极为巧妙&#xff0c;也很值得借鉴. 概述 重要数据结构 AVBufferPool 在libavutil/buffer_internal.h中定义&#xff0c;为内部数据结构&#xff0c;不能在应用程序中直…

基于Java的XxlCrawler网络信息爬取实战-以中国地震台网为例

目录 前言 一、信息网站介绍 1、网站介绍 2、 地震历史信息 3、 历史信息接口分析 二、XxlCrawler组件 1、关于XxlCrawler 2、核心概念介绍 三、实际信息爬取 1、新建maven项目 2、新建model层对象 3、实际爬取 总结 前言 如今&#xff0c;只要谈起网络信息爬取也就…

TCP/IP协议—HTTP

TCP/IP协议—HTTP HTTP协议HTTP通讯特点HTTP通讯流程 HTTP请求报文请求方法 HTTP应答报文状态码 HTTP协议 超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff0c;HTTP&#xff09;是一种请求-响应的协议&#xff0c;用户可以通过HTTP向服务器上传、下载数据。HT…