Vue3+mitt.js配置logger

news2025/2/28 17:16:14

前言

Vue项目中的组件通信方式,绝大多数的情况是可以被Vuex等方案代替的,但有一些特殊情况却非常适合使用EventBus,举个简单的例子:有A、B两个组件,用户在A上进行操作后,需要B执行某些逻辑。

由于Vue3中删除了$on、$once、$off等方法,官方推荐使用 mitt.js 替代已经移除的EventBus。

封装原因

1.若项目中大量使用mitt进行组件通信,容易造成数据混乱,以及逻辑分散,出现问题很难定位、溯源。基于以上原因,对mitt配置logger,达到操作显化的效果。

2.若emit抛出一个事件后,需要等待on捕捉事件并返回完结状态后(也就是异步操作),再执行相关逻辑,因此需要扩展一个emitAsync方法。举例:在组件A中点击按钮,通知组件B中的modal弹出给予用户进行二次确认,用户点击确认 => 组件A执行确认操作,用户点击取消 => 组件A执行取消操作


具体代码

import mitt from 'mitt'

// 当前文件名 需要同步修改
const CURRENT_FILE_NAME = 'useMitt'

// 基础配置
const defineOptions = {
  // 是否禁用
  logDisabled: false,
  // 是否展开
  logExpanded: false,
  // console.group样式
  logStyle: 'color: #fff; background: #51a2e4; font-size: 12px; padding: 4px; border-radius: 4px'
}

// uuid
let idCounter = 0;
const uniqueId = (prefix) => {
  return prefix.toString() + ++idCounter;
}

// 操作时间 HH:mm:ss:ms
const formatTime = (date = new Date()) => {
  const hours = date.getHours().toString().padStart(2, '0');
  const minutes = date.getMinutes().toString().padStart(2, '0');
  const seconds = date.getSeconds().toString().padStart(2, '0');
  const milliseconds = date.getMilliseconds().toString();

  return `${hours}:${minutes}:${seconds}:${milliseconds}`;
}

// 获取堆栈
const getStackTrace = () => {
  let stackStr = ''
  let stackArr = []
  // 通过Error获取
  try {
    throw new Error('');
  }
  // catch掉才不会被博睿监控到
  catch (error) {
    stackStr = error.stack || '';
  }
  // 字符串信息分割为数组 并去掉空格
  stackArr = stackStr.split('\n').map((line) => line.trim());
  return stackArr.splice(stackArr[0] === 'Error' ? 2 : 1);
}

// 在堆栈信息数组中 匹配触发事件的函数
const matchFnCaller = (stackTrace = []) => {
  // 文件在src下的 且排除本文件 没有匹配的返回第0项
  return stackTrace.find(item => item.includes('src') && !item.includes(CURRENT_FILE_NAME)) || stackTrace[0]
}


// 事件信息map结构 key: eventName value: { count: 触发次数, describe: 事件描述 }
const eventMap = new Map()

// 监听事件 配置logger
const handleOnEvent = (eventName, value, options) => {
  const stackTrace = getStackTrace()

  // 匹配事件源 去掉 at 字符
  const source =  matchFnCaller(stackTrace).substring(3)
  const describe = eventMap.get(eventName).describe
  const count = eventMap.get(eventName).count

  const g = options.logExpanded ? console.group : console.groupCollapsed
  g(`%cmittId => ${options.id}`, options.logStyle)
  console.log('eventName:', eventName)
  console.log('eventValue:', value)
  console.log('eventDescribe:', describe)
  console.log('eventSource:', source)
  console.log('count:', count)
  console.log('time:', formatTime())
  console.groupEnd()
}

// 记录事件信息
const markEventMap = (type, describe) => {
  if (eventMap.has(type)) {
    let event = eventMap.get(type)
    eventMap.set(type, {
      count: ++event.count,
      describe
    })
  } else {
    eventMap.set(type, {
      count: 1,
      describe
    })
  }
  console.log('eventMap', eventMap)
}
// emit事件 记录事件信息
const handleEmitEvent = (emitter) => {
  const originEmit = emitter.emit
  emitter.emit = (type, e, describe = '') => {
    markEventMap(type, describe)
    originEmit(type, e)
  }
}

// 增加emitAsync方法
// https://github.com/developit/mitt/discussions/157
const emitAsync = async function(type, e, describe) {
  // 记录事件信息
  markEventMap(type, describe)

  let handlers = this.all.get(type)
  if (handlers) {
    for (const f of handlers) {
      await f(e)
    }
  }
  handlers = this.all.get('*')
  if (handlers) {
    for (const f of handlers) {
      await f(type, e)
    }
  }
}
const mittAsync = (all) => {
  const instance = mitt(all)
  instance.emitAsync = emitAsync
  return instance
}

export default function useMitt(customOption) {
  // 配置项
  const options = Object.assign({
    id: uniqueId('mitt-')
  }, defineOptions, customOption)

  // 实例化mitt
  const emitter = mittAsync()

  if (!options.disabled) {
    // 监听所有事件,配置logger
    emitter.on('*', (eventName, value) => handleOnEvent(eventName, value, options))
    // 劫持emit,记录信息
    handleEmitEvent(emitter)
  }
 
  return emitter
}

emitAsync异步调用

import useMitt from '@/hooks/useMitt'

const emitter = useMitt({id: 'listEmitter', logExpanded: true})
const handleClick = () => {
        emitter.emitAsync('showModal', 'data', '此函数是激活confirm modal')
          .then(() => {
            console.log('confirm')	// resolve后打印
          })
          .catch(() => {
            console.log('cancel')	// reject后打印
          })
      }
emitter.on('showModal', () => {
        return new Promise((resolve, reject) => {
          Modal.confirm({
            title: '确认是否XXX',
            onOk() {
              resolve()
            },
            onCancel() {
              reject()
            }
          })
        })
      })

logger效果

import useMitt from '@/hooks/useMitt'
const emitter = useMitt({ id: contract-edit, logExpanded: true })
const handleClick = () => {
        emitter.emit('click', 'data', '这是一个点击事件')
      }

logger打印效果
点击eventSource中的链接,定位到触发位置
logger定位调用栈


注意

需要将实例化的emitter通过provide或其他方式传给其他组件,才能相互通信。
也可以自行改造,加上单例模式解决上述问题

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

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

相关文章

TiDB在转转公司的发展历程

1 前言 2 运维痛点 3 解决痛点 3.1 元数据管理 3.2 机器资源管理 3.3 全面升级 3.4 告警改造 4 实现自动化 4.1 需求工单化 4.2 操作平台化 4.3 其他辅助功能 5 写在最后 1 前言 转转是PingCAP最早的一批用户之一,见证了TiDB的发展,自身也沉淀…

计讯物联高精度定位GNSS接收机TN521在水库大坝变形监测的应用解析

由于水库大坝在地质环境恶劣和气候条件复杂的条件下,水库坝体的稳定性会受到严重考验。为了保证水库大坝的安全运营,GNSS作为一种实现远程自动化测量的高精度的变形监测技术,具有高精度、高速度、全天候、连续实时、自动化等优势,…

WMS系统推荐,如何选到适合企业的仓库管理系统

市场上有很多WMS系统,但是现在很多仓库管理系统都在使用WMS系统。那么在选择WMS系统时应该考虑什么呢?明确业务发展特征,准确表达能力目标许多物流企业在选择物流管理系统时,往往会被物流管理系统的整体系统所迷惑,在功…

MySql的sql语句执行过程详述

目录 前言: sql语句的执行过程: server层: 存储引擎: 连接器: 查询缓存: 分析器: 优化器: 执行器: 前言: 很多人都在使用mysql数据库,但…

【软件测试】测试人的bug一生,资深测试的专业角度......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 对于测试人员来说&a…

【Linux】进程的描述组织与进程状态

文章目录🎪 进程的描述组织🚀1.什么是进程🚀2.进程的形成🚀3.进程标识符 *⭐3.1 PS命令查看PID⭐3.2 /proc目录查看进程属性🚀4.父子进程⭐4.1 系统调用获取PID⭐4.2 fork创建子进程⭐4.3 fork双返回值问题⭐4.4 写时拷…

Netty源码解读-EventLoop(二)

一、简介 NioEventLoop的重要组成:Selector、线程、任务队列,他既会处理io事件,也会处理普通任务和定时任务. 1.下面是Selector,注意有两个哦后面会讲 2.下面的爷爷类提供的Thread变量,其实下面发excutor用的就是这个…

vue2中defineProperty和vue3中proxy区别

区别一:defineProperty 是对属性劫持,proxy 是对代理对象 下面我们针对一个对象使用不同的方式进行监听,看写法上有什么不同。 // 原始对象 const data {name: Jane,age: 21 }defineProperty defineProperty 只能劫持对象的某一个属性&…

TCP核心机制详解(三)

目录 前言: 滑动窗口 滑动窗口处理丢包问题 流量控制 拥塞控制 延时应答 捎带应答 面向字节流 异常情况 小结: 前言: 前两篇文章讲述了,TCP十种核心机制的前三种。这篇文章详细介绍其他的一些核心机制,让我们…

大气温室气体浓度不断增加,导致气候变暖加剧,随之会引发一系列气象、生态和环境灾害怎样解决?

大气温室气体浓度不断增加,导致气候变暖加剧,随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物(特别是红树林、盐沼和海草&…

企企通持续助力全球管道预制先行者「迈科管道」,二期项目逐步启动

近日,国内管道预制龙头企业「济南迈科管道科技有限公司」签约企企通采购供应链系统二期项目。此次签约,将针对迈科管道的采购业务特点和需求,结合企企通采购与供应链管理系统优势和丰富的实战经验,在一期项目基础上,持…

山东科技大学校历 代码分析 获得以前学期学年的老版校历

校历-山东科技大学网络安全与信息化办公室http://tech.sdust.edu.cn/wlfw/xl.htm JavaScript var studyStart new Date(2021, 8-1, 30);//8-1 -1没有意义,30代表30号,2021年8月30号开始这个学期 var commonWeeks 19;//这个学期有多少周 var s…

Mysql使用规范(纯技术和实战建议)

1、事务隔级别: (强制):Repeatable-Read(重复读),且不能在会话操作时临时开启隔离级别。 注: Repeatable-Read(重复读)隔离级别解决不了幻读。 可用 show variables l…

Linux学习之环境变量

目录环境变量基本概念查看环境变量的方法常见的环境变量PATH:指定命令的搜索路径。PATH测试HOME:指定用户的主工作目录SHELL:当前Shell和环境变量有关的命令环境变量的组织方式通过代码来获得环境变量通过函数来获得环境变量环境变量具有全局…

集成AI智能安防平台EasyCVR时,调取录像接口报跨域错误的解决方法

EasyCVR视频融合平台基于云边端架构,可支持海量视频汇聚管理,能提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、智能分析等视频服务。平台兼容性强,支持多协议、多类型设备接入,包括:国标G…

Datawahle组队学习——妙趣横生大数据 Day1

妙趣横生大数据 Day1[妙趣横生大数据 Juicy Big Data](https://datawhalechina.github.io/juicy-bigdata/#/?id妙趣横生大数据-juicy-big-data)一、大数据概述大数据——第三次信息化浪潮大数据概念大数据应用大数据关键技术二、Hadoop背景介绍特性项目架构实验1. 准备工作2. …

Tomcat 配置IPV6

文章目录一、场景二、tomcat开启ipv6三、ipv6环境配置四、访问总结一、场景 我们在linux下安装一个tomcat,启用ipv6的方式,然后在windows下用浏览器访问这个tomcat 二、tomcat开启ipv6 在server.xml配置文件的里面加上 address”[::]” ,这…

选择排序、快速排序、插入排序等经典八大算法稳定性分析

选择排序、快速排序、插入排序等经典八大算法稳定性分析稳定性定义:各排序算法的稳定性:一、冒泡排序二、选择排序三、插入排序四、快速排序五、归并排序六、希尔排序(shell)七、基数排序 :八、堆排序常用排序算法对比稳定性定义:…

代码的简单设计五原则

欢欢:“你看我的代码用了策略模式和状态模式,假如后面客户会有这样的需求,可以无缝扩展,多么健壮!” 清扬一脸狐疑,心中念叨了数遍 :“哼,过度设计!”,只见她…