前端(Electron)事件系统设计第三章

477 阅读3分钟

如何给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这个注册函数,根据我们Emitterevent实现来看,它会返回一个具有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的入参