什么是事件發(fā)射器(Event Emitter)?
朋友們,作為一名軟件工程師,你一定用過Event Emitter,我們經(jīng)常用它來處理跨組件的通信場景。
它觸發(fā)了一個(gè)每個(gè)人都可以收聽的事件,并且可以在事件觸發(fā)時(shí)發(fā)送數(shù)據(jù)。
不同的庫提供不同的實(shí)現(xiàn),用于不同的目的,但基本思想是提供一個(gè)用于發(fā)布和訂閱事件的框架。
你想知道它背后的魔力嗎?本文將與你分享一個(gè)非常簡單的解決方案來實(shí)現(xiàn)它。
我們一起來試試。
請用以下這個(gè)例子來玩一會(huì)兒。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
html, body{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.box{
padding-top: 30px;
}
</style>
</head>
<body>
<div class="buttons">
<button>Please send me data</button>
<button>Cut off contact</button>
</div>
<div class="box">
The data you sent me is:
<div class="data"></div>
</div>
<script>
class EventEmitter {
on = (eventName, callback) => window.addEventListener(eventName, callback, false)
off = (eventName, callback) => window.removeEventListener(eventName, callback, false)
emit = (eventName, data) => window.dispatchEvent(new CustomEvent(eventName, { detail: data }))
}
const emitter = new EventEmitter()
const buttons = document.querySelectorAll('button')
const $data = document.querySelector('.data')
let count = 0
const listentCallback = () => {
$data.innerHTML = JSON.stringify(event.detail, null, 2)
}
emitter.on('event-fatfish', listentCallback)
buttons[0].addEventListener('click', () => {
count++
emitter.emit('event-fatfish', { name: 'fatfish', count })
})
buttons[1].addEventListener('click', () => {
emitter.off('event-fatfish', listentCallback)
})
</script>
</body>
</html>
輸出:

當(dāng)你點(diǎn)擊 Please send me data 按鈕時(shí),你會(huì)看到 count 的值越來越大,但是在你點(diǎn)擊 Cut off contact 之后,它就不再變化了。
這個(gè)例子很簡單,但足以說明有關(guān) Event Emitter 的一切。
來,我們開始吧!
Event Emitter 只需幾行代碼就可以完成,這真是太神奇了。
class EventEmitter {
on = (eventName, callback) => window.addEventListener(eventName, callback, false)
off = (eventName, callback) => window.removeEventListener(eventName, callback, false)
emit = (eventName, data) => window.dispatchEvent(new CustomEvent(eventName, { detail: data }))
}
1. 監(jiān)聽事件
const emitter = new EventEmitter()
const eventCallback = (event) => {
console.log('eventCallback', event.detail)
}
emitter.on('event-xxx', eventCallback)
2. 發(fā)布事件
eventCallback 將打印兩次數(shù)據(jù),因?yàn)槲覀儍纱伟l(fā)布了 event-xxx 事件。
emitter.emit('event-xxx', { name: 'fatfish' })
emitter.emit('event-xxx', { name: 'medium' })

3.解除事件
當(dāng)我們解除 event-xxx 事件時(shí),不再打印 medium 和 fatfish。
emitter.off('event-xxx', eventCallback)
emitter.emit('event-xxx', { name: 'medium and fatfish' })

CustomEvent 是謎題的答案
實(shí)現(xiàn) EventEmitter 的關(guān)鍵是 CustomEvent 和瀏覽器的事件機(jī)制,你可以從這里得到:https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent。
CustomEvent() 構(gòu)造函數(shù)創(chuàng)建一個(gè)新的 CustomEvent 對象。——來自 MDN
// create custom events
const catFound = new CustomEvent('animalfound', {
detail: {
name: 'cat'
}
})
const dogFound = new CustomEvent('animalfound', {
detail: {
name: 'dog'
}
})
// add an appropriate event listener
window.addEventListener('animalfound', (e) => console.log(e.detail.name))
// dispatch the events
window.dispatchEvent(catFound)
window.dispatchEvent(dogFound)

實(shí)現(xiàn)事件發(fā)射器的另一種方法
雖然,這種方法很簡單,但它依賴于瀏覽器環(huán)境,還有其他更好的解決方案嗎?
class EventEmitter {
constructor () {
this.events = {}
}
on (evt, callback, ctx) {
if (!this.events[ evt ]) {
this.events[ evt ] = []
}
this.events[ evt ].push(callback)
return this
}
emit (evt, ...payload) {
const callbacks = this.events[ evt ]
if (callbacks) {
callbacks.forEach((cb) => cb.apply(this, payload))
}
return this
}
off (evt, callback) {
// Cancel all subscribed events
if (typeof evt === 'undefined') {
delete this.events
} else if (typeof evt === 'string') {
// Delete the subscriber of the specified event
if (typeof callback === 'function') {
this.events[ evt ] = this.events[ evt ].filter((cb) => cb !== callback)
} else {
// Delete event directly
delete this.events[ evt ]
}
}
return this
}
}
const e1 = new EventEmitter()
const e1Callback = (name) => {
console.log(name, 'e1Callback')
}
const e2Callback = (name, sex) => {
console.log(name, 'e2Callback')
}
e1.on('evt1', e1Callback)
e1.on('evt2', e2Callback)
e1.emit('evt1', 'fatfish') // fatfish e1Callback
e1.emit('evt2', 'medium') // medium e2Callback
e1.off('evt1', e1Callback)
e1.emit('evt1', 'fatfish') // fatfish e1Callback will not be printed
e1.emit('evt2', 'medium') // medium e2Callback

寫在最后
以上就是我今天跟你分享的關(guān)于事件發(fā)射器的全部內(nèi)容,不知道你還有沒有其他更好的實(shí)現(xiàn)方法?如果有的話,請記得在留言區(qū)跟我分享你的解決方案,在此,非常感謝。
看完今天內(nèi)容,如果你覺得有用的話,請記得點(diǎn)贊我,關(guān)注我,并將這篇內(nèi)容分享給你的朋友們,也許能夠幫助到他。
最后,感謝你的閱讀,編程愉快!