用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。