用Ts优雅的封装EventEmitter

用Ts优雅的封装EventEmitter

很多学前端的人其实都知道EventEmitter,其实就算一个事件订阅器,

这东西可以很简单的实现前端的通讯,不需要使用什么组件。

我之前有了解过cocos Createor内的脚本代码,里面游戏实例组件之间通讯也有使用EventEmitter。

EventEmitter js版本

这是我封装的

/**
 * 事件发送器
 */
class EventEmitter {
  events = new Map()
  /**
   * 事件监听
   * @param key
   * @param fn
   * @returns
   */
  on (key, fn) {
    this._on(key, fn, false)
  }
  /**
   * 事件监听(只监听一次)
   * @param key
   * @param fn
   * @returns
   */
  once (key, fn) {
    this._on(key, fn, true)
  }
  _on (key, fn, once) {
    let item = this.events.get(key)
    if (item) {
      item.push({
        fn,
        once,
      })
      return
    }
    this.events.set(key, [
      {
        fn,
        once,
      },
    ])
  }
  /**
   * 事件发布
   * @param key
   * @param fn
   * @returns
   */
  emit (key, ...args) {
    let list = this.events.get(key)
    if (!list) return
    list.forEach((item) => {
      // 加个try防止报错堵塞
      try {
        item.fn(...args)
      } catch (e) {
        console.error(e)
      }
    })
    list = list.filter((item) => !item.once)
    this.events.set(key, list)
  }
  /**
   * 取消监听
   * @param key
   * @param fn
   * @returns
   */
  off (key, fn) {
    let list = this.events.get(key)
    if (!list) return
    list = list.filter((item) => item.fn !== fn)
    this.events.set(key, list)
  }
}

export default EventEmitter

使用案例
const eventEmitter = new EventEmitter();
//username和msg设置的类型不是正确的
const onMessage = (username:number,msg:number)=>{
	console.log(username+":"+msg);
}	
//注册监听
eventEmitter.on('onMessage',onMessage);

//抛出事件
eventEmitter.emit('onMessage',"wei","你好")
//注销监听
eventEmitter.off('onMessage',onEmit)

在这种情况下 username和msg设置的类型不是正确的,但是这并不会报错

EventEmitter ts版本

在使用ts由你会发现,在调用注册事件时或发出事件时没有类型限制,

假如你在项目的另一个文件抛出了一个事件,这个事件的事件名字参数类型参数个数 这些都没有进行限制。

虽然你知道类型是什么,但是如果项目很大,不可能全部记得这些信息,你只能再去抛出事件的文件位置查看

我们对EventEmitter.js进行ts标注

/**
 * 任何函数
 */
type WhateverFunc = (...args: any[]) => any
/**
 * 这里限制所有的 key只能是函数类型
 */
interface EventType {
  [key: string]: WhateverFunc
  [numberKey: number]: WhateverFunc
  [SymbolKey: symbol]: WhateverFunc
}

interface EventItem {
  fn: WhateverFunc
  once: boolean
}
/**
 * 事件发送器
 */
class EventEmitter<T extends EventType> {
  private events: Map<keyof T, EventItem[]> = new Map()
  /**
   * 事件监听
   * @param key
   * @param fn
   * @returns
   */
  on<K extends keyof T>(key: K, fn: T[K]) {
    this._on(key, fn, false)
  }
  /**
   * 事件监听(只监听一次)
   * @param key
   * @param fn
   * @returns
   */
  once<K extends keyof T>(key: K, fn: T[K]) {
    this._on(key, fn, true)
  }
  private _on<K extends keyof T>(key: K, fn: T[K], once: boolean) {
    let item = this.events.get(key)
    if (item) {
      item.push({
        fn,
        once,
      })
      return
    }
    this.events.set(key, [
      {
        fn,
        once,
      },
    ])
  }
  /**
   * 事件发布
   * @param key
   * @param fn
   * @returns
   */
  emit<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {
    let list = this.events.get(key)
    if (!list) return
    list.forEach((item) => {
      // 加个try防止报错堵塞
      try {
        item.fn(...args)
      } catch (e) {
        console.error(e)
      }
    })
    list = list.filter((item) => !item.once)
    this.events.set(key, list)
  }
  /**
   * 取消监听
   * @param key
   * @param fn
   * @returns
   */
  off<K extends keyof T>(key: K, fn: T[K]) {
    let list = this.events.get(key)
    if (!list) return
    list = list.filter((item) => item.fn !== fn)
    this.events.set(key, list)
  }
}

export default EventEmitter

这样封装后我们只需要在使用的时候定义一个限制类型,就可以直接使用了

使用案例
type MessageEventType={
	onMessage:(username:string,msg:string)=>void,
	onError:(err:string)=>void,
}

const eventEmitter = new EventEmitter<MessageEventType>()


const onMessage = (username:string,msg:string)=>{
	console.log(username+":"+msg);
}	
//注册监听 ->  onMessage的类型必须和MessageEventType.onMessage类型保持一致才不报错
eventEmitter.on('onMessage',onMessage);

//抛出事件
eventEmitter.emit('onMessage',"wei","你好呀")
//注销监听
eventEmitter.off('onMessage',onMessage)

onMessage的类型必须和MessageEventType.onMessage类型保持一致才不报错

很多问题在运行前能发现可以解决很多的bug。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序老六

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值