EventEmitter3 模塊是一款通過優化的高性能 EventEmitter,而 EventEmitter 是一種發佈/訂閱模式的實現方式,經過使用 EventEmitter 咱們可以在前端代碼中建立自定義的事件機制。前端
EventEmitter3 模塊同時兼融 NodeJS 和 瀏覽器npm
npm install EventEmitter3 --save
複製代碼
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); // 移除現有的訂閱
複製代碼
在咱們編寫複雜的邏輯時,可能會受制於框架、上下文、工具只能採起異步的編碼,而爲了提升事務調度器的代碼可讀性,咱們能夠經過 EventEmitter + Promise + async&await 來將主邏輯代碼以同步的方式編寫在同一個上下文、代碼塊中。 例如筆者在使用 React Hooks 編寫一些複雜的組件時,由於 useState 返回的更新函數並不像 Class組件的 SetState 同樣提供新狀態已被成功渲染的回調函數,因此只能配合 useEffect 來處理渲染完成的信號。但這樣子咱們的邏輯代碼會被迫拆分到了多個代碼塊中,致使代碼過於複雜,可讀性、維護性都下降,例如如下代碼:canvas
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>
)
}
複製代碼
接下來,讓咱們優化咱們的代碼:瀏覽器
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>
)
}
複製代碼