前言
EventEmitter3 模块是一款经过优化的高性能 EventEmitter,而 EventEmitter 是一种发布/订阅模式的实现方式,通过使用 EventEmitter 我们能够在前端代码中创建自定义的事件机制。
EventEmitter3 模块同时兼融 NodeJS 和 浏览器
使用 EventEmitter3
安装
1 | npm install EventEmitter3 --save |
引用
1 2 3 4 5 6 7 8 9 10 11 | var EE = new EventEmitter() , context = { foo: 'bar' }; function emitted() { console.log(this === context); // true } // 订阅 EE.once('event-name', emitted, context); // 只订阅一次 EE.on('another-event', emitted, context); // 持续订阅 EE.removeListener('another-event', emitted, context); // 移除现有的订阅 |
骚操作
配合 Promise/async&await 封装同步代码
在我们编写复杂的逻辑时,可能会受制于框架、上下文、工具只能采取异步的编码,而为了提高事务调度器的代码可读性,我们可以通过 EventEmitter + Promise + async&await 来将主逻辑代码以同步的方式编写在同一个上下文、代码块中。
例如笔者在使用 React Hooks 编写一些复杂的组件时,因为 useState 返回的更新函数并不像 Class组件的 SetState 一样提供新状态已被成功渲染的回调函数,所以只能配合 useEffect 来处理渲染完成的信号。但这样子我们的逻辑代码会被迫拆分到了多个代码块中,导致代码过于复杂,可读性、维护性都降低,例如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function MyComponent() { const [size, setSize] = useState({ width: 0, height: 0 }); useEffect(() => { // 在这里对 canvas 进行 draw 的操作才是安全的 }, [size]); return ( <div> <button onClick={() => { setSize({ width: 100, height: 100 }); // 这里对 canvas 进行 draw 等操作可能不安全,因为此时 Size 变更后并没有实际应用到 dom 中,所以很可能会 draw 后 dom 中的 canvas 会重新渲染,而 canvas 重新渲染后其中绘制的内容会被重置。 }}>init canvas</button> <canvas style={{ width: `${size.width}px`, height: `${size.height}px` }}></canvas> </div> ) } |
接下来,让我们优化我们的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | const EE = new EventEmitter(); function MyComponent() { const [size, setSize] = useState({ width: 0, height: 0 }); // 封装同步更新方法 const setSizeSync = (value) => { setSize(value); return new Promise(resolve => { EE.once('onSizeChange', resolve); // 订阅一次 "onSizeChange" 事件 }); } useEffect(() => { EE.emit('onSizeChange'); // 触发 "onSizeChange" 事件 }, [size]); return ( <div> <button onClick={async () => { await setSizeSync({ width: 100, height: 100 }); // 现在这里可以安全的使用 CanvasContext 对 Canvas 进行操作了 }}>init canvas</button> <canvas style={{ width: `${size.width}px`, height: `${size.height}px` }}></canvas> </div> ) } |