如何给ipcMain赋予我们自己实现的Emitter的能力
我们就采用装饰者的思想,将能力装饰给了ipcMain
export namespace Event {
// emitter: NodeEventEmitter 相当于参数:ipcMain
// eventName: string 相当于参数:'ipc:hello'
export function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string) => T = id => id): Event<T> {
const fn = (...args: any[]) => result.fire(...args);
const onFirstListenerAdd = () => emitter.on(eventName, fn);
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
return result.event;
}
}
在解读这个函数之前,我们先来看下原始的electron进程通信
基本使用与采用此装饰后的写法:
// 主进程
ipcMain.on('ipc:hello', (event, params) => {
console.log('注册了一个关于`ipc:hello`事件的回调:', params);
})
// 渲染进程中
webcontents.send('ipc:hello', params);
装饰后的使用:
// 主进程
const onHello = Event.fromNodeEventEmitter<Electron.WebContents>(ipcMain, 'ipc:hello');
// 此时就注册了监听:'ipc:hello'事件的一个回调函数。
const remove = onHello(() => console.log('注册了一个关于`ipc:hello`事件的回调'));
// 渲染进程
webcontents.send('ipc:hello', params);
结合上述使用,现在我们来解读这个函数fromNodeEventEmitter
:
const onFirstListenerAdd = () => emitter.on(eventName, fn);
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
我们使用了自己实现的Emitter
,当第一个监听被注册的时候,我们开始监听原始的事件:
// 根据参数,相当于这句:ipcMain.on(eventName, fn)
emitter.on(eventName, fn)
此时,但凡有'ipc:hello'消息过来,就会执行函数fn
,此时就会触发emitter.fire
:
const fn = (...args: any[]) => result.fire(...args);
这样,使用被装饰后的emitter
(onHello)注册的事件都会被执行,比如:
const remove = onHello(() => console.log('注册了一个关于`ipc:hello`事件的回调'));
此时会打出:'注册了一个关于ipc:hello事件的回调'
.
由于onHello
这个注册函数,根据我们Emitter
的event
实现来看,它会返回一个具有dispose
方法的对象。因此,我们可以这样移除监听:
remove.dispose();
当onHello
监听的所有函数都移除时,就会触发onLastListenerRemove
,从而移除ipcMain
的监听。
// 相当于:ipcMain.removeListener('ipc:hello');
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
以上就解读完了fromNodeEventEmitter
这个函数。
那么问题来了,我们这么装饰一波有啥用呢,可能大家有一个发现:
onHello
这个函数,已经和'ipc:hello'事件绑定了- 可以一次性remove所有listener
所以它不需要像:ipcMain.on('ipc:hello', handler)
要传递一个事件名,
而是直接:onHello(handler)
就注册上了事件。
所以,这就是我们装饰后的好处,此时的emitter与一个具体的事件绑定了,并且可以一次清理所有listener,但费了如此周章,就为了这玩意?搞鸡毛!
所以,肯定不止于此,请思考如下场景:
- 我想给监听'ipc:hello'事件的所有handler统一处理
入口参数
怎么办。 - 我想只有当指定的人发送过来的
onHello
,我才接收怎么办。 - 我想只接收符合某些条件的人的
onHello
怎么办 - 我想只接受符合某些条件的第一个人的
onHello
怎么办
请听下回分解:统一处理事件listener
的入参