目录
- 背景介绍
- 使用方法
- 源码分析
背景介绍
近期业务开发,会想起之前做的两个组件的通信,社区推荐使用一个迷你库miit (200b), 因为vue3开发中没有了 EventBus 跨组件通信,这个替代的方案 mitt.js,原理还是 EventBus
EventBus在多个组件之间进行事件通信的场景下还是比较有用的,通过监听事件和触发事件,可以在订阅者和发布者之间解耦,实现一个常规的eventBus也比较简单
以上实例是组件B想和组件C通信,但是mitt不管组件嵌套多深都可以直接拿来用
使用方法
先安装 npm i mitt -S
到项目中,然后像以前封装EventBus一样封装
// 可以在项目目录utils下封装一个event.js | |
import mitt from 'mitt' | |
const mitt = mitt() | |
export default mitt |
业务逻辑组件中通信使用
// 组件 A | |
<script setup> | |
import mitt from '@/utils/event.js' | |
function handleChange(obj) { | |
mitter.emit('search-change', obj); | |
} | |
</script> | |
// 组件 B | |
<script setup> | |
import mitt from '@/utils/event.js' | |
import { onUnmounted ,onMounted} from 'vue' | |
// 监听 | |
onMounted(()=>{ mitt.on('search-change',(obj)=> { do sth} }) | |
// off 监听 | |
onUnmounted(()=>{ mitt.off('search-change', ()=> { do sth } }) | |
</script> |
源码分析
一行行看,看懂每一行的写法和整体思想
export type EventType = string | symbol; | |
// 源码第一段分析 | |
export 一个类型别名 EventType,上面例子中我们mitter.emit('search-change', obj); | |
search-change就是一个string类型, | |
同时EventType也可以是联合类型symbol,为了保证事件的唯一性 | |
export type Handler<T = unknown> = (event: T) => void; | |
export type WildcardHandler<T = Record<string, unknown>> = ( | |
type: keyof T, | |
event: T[keyof T] | |
) => void; | |
// 源码第二段分析 | |
使用类型别名定义函数Handler,Handler接受一个泛型参数<T>,默认值是unknown | |
export type EventHandlerList<T = unknown> = Array<Handler<T>>; | |
export type WildCardEventHandlerList<T = Record<string, unknown>> | |
= Array<WildcardHandler<T>>; | |
// 源码第三段分析 | |
当前所有注册的事件列表类型定义 Array<T>数组的泛型定义 | |
export type EventHandlerMap<Events extends Record<EventType, unknown>> | |
= Map<keyof Events | '*',EventHandlerList<Events[keyof Events]> | |
| WildCardEventHandlerList<Events>>; | |
// 源码第四段分析 | |
这里是事件类型及其对应的事件处理程序的映射Map做了定义,使用extend继承一个Record对象, | |
该对象的两个参数一个是限制为Events上已知的公共属性名的联合或者*, | |
另外一个参数要是是EventHandlerList或者是WildCardEventHandlerList | |
export interface Emitter<Events extends Record<EventType, unknown>> { | |
all: EventHandlerMap<Events>; | |
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) | |
: void; | |
on(type: '*', handler: WildcardHandler<Events>):void; | |
off<Key extends keyof Events>(type: Key, handler?: | |
Handler<Events[Key]>): void; | |
off(type: '*', handler: WildcardHandler<Events>):void; | |
emit<Key extends keyof Events>(type: Key, event: Events[Key]):void; | |
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? | |
Key : never):void; | |
} | |
// 源码第五段分析 | |
interface Emitter //用于下面一段核心代码的返回值类型定义,这个interface定义了具体函数的结构类型 | |
Emitter //这个类型的泛型是Events继承一个Record对象,该对象的key为EventType,value为unknown | |
//导出了一个 mitt([all])函数,调用该函数返回一个 Emitter,该对象包含all、 | |
on(type, handler)、off(type, [handler])和emit(type, [evt])这几个属性 | |
/** | |
* Mitt: Tiny (~200b) functional event emitter / pubsub. | |
* @name mitt | |
* @returns {Mitt} | |
*/ | |
export default function mitt<Events extends Record<EventType, unknown>>( | |
all?: EventHandlerMap<Events> | |
): Emitter<Events> { | |
type GenericEventHandler = | |
| Handler<Events[keyof Events]> | |
| WildcardHandler<Events>; | |
all = all || new Map(); | |
return { | |
/** | |
* A Map of event names to registered handler functions. | |
*/ | |
all, | |
/** | |
* Register an event handler for the given type. | |
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events | |
* @param {Function} handler Function to call in response to given event | |
* @memberOf mitt | |
*/ | |
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) { | |
const handlers: Array<GenericEventHandler> | undefined = | |
all!.get(type); | |
if (handlers) { | |
handlers.push(handler); | |
} else { | |
all!.set(type, [handler] as EventHandlerList< | |
Events[keyof Events]>); | |
} | |
}, | |
/** | |
* Remove an event handler for the given type. | |
* If `handler` is omitted, all handlers of the given type are removed. | |
* @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler) | |
* @param {Function} [handler] Handler function to remove | |
* @memberOf mitt | |
*/ | |
off<Key extends keyof Events>( type: Key, | |
handler?: GenericEventHandler | |
) { | |
const handlers: Array<GenericEventHandler> | undefined = | |
all!.get(type); | |
if (handlers) { | |
if (handler) { | |
handlers.splice(handlers.indexOf(handler) >>> 0, 1); | |
} else { | |
all!.set(type, []); | |
} | |
} | |
}, | |
/** | |
* Invoke all handlers for the given type. | |
* If present, `'*'` handlers are invoked after type-matched handlers. | |
* | |
* Note: Manually firing '*' handlers is not supported. | |
* | |
* @param {string|symbol} type The event type to invoke | |
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler | |
* @memberOf mitt | |
*/ | |
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) { | |
let handlers = all!.get(type); | |
if (handlers) { | |
(handlers as EventHandlerList<Events[keyof Events]>) | |
.slice() | |
.map((handler) => { | |
handler(evt!); | |
}); | |
} | |
handlers = all!.get('*'); | |
if (handlers) { | |
(handlers as WildCardEventHandlerList<Events>) | |
.slice() | |
.map((handler) => { | |
handler(type, evt!); | |
}); | |
} | |
} | |
}; | |
} |
核心代码主要实现就是:
1.all = all || new Map() mitt 支持传入 all 参数用来存储事件类型和事件处理函
数的映射Map,如果不传,就 `new Map()`赋值给 all
2.on(type, handler)定义函数 on来注册事件,以type为属性,[handler]为属性值,
存储在 all 中,属性值为数组的原因是可能存在监听一个事件,多个处理程序
3.off(type, [handler])来取消某个事件的某个处理函数,根据 type 找到对应的事件处理数组,
对比 handler 是否相等,相等则删除该处理函数,不传则删除该事件的全部处理函数
4.emit(type, [evt])来派发事件,根据 type 找到对应的事件处理数组并依次执行,传入参数 evt(对象最好,传多个参数只会取到第一个)