【设计模式】前端必懂EventEmitter
本文說(shuō)一下 EventEmitter,比較簡(jiǎn)單,可以直接看代碼。
發(fā)布 + 訂閱
DOM 的事件機(jī)制就是發(fā)布訂閱模式最常見(jiàn)的實(shí)現(xiàn),這大概是前端最常用的編程模型了,監(jiān)聽(tīng)某事件,當(dāng)該事件發(fā)生時(shí),監(jiān)聽(tīng)該事件的監(jiān)聽(tīng)函數(shù)被調(diào)用。
發(fā)布訂閱模式,阮一峰在《Javascript 異步編程的 4 種方法》,中:
我們假定,存在一個(gè)"信號(hào)中心",某個(gè)任務(wù)執(zhí)行完成,就向信號(hào)中心"發(fā)布"(publish)一個(gè)信號(hào),其他任務(wù)可以向信號(hào)中心"訂閱"(subscribe)這個(gè)信號(hào),從而知道什么時(shí)候自己可以開(kāi)始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern),又稱(chēng)"觀察者模式"(observer pattern)。
感興趣的可以去看看,下圖是我平時(shí)在用的一個(gè)實(shí)現(xiàn)。(重點(diǎn)看)
class EventEmitter {constructor() {this._events = Object.create(null);}on(type, handler) {(this._events[type] || (this._events[type] = [])).push(handler);}off(type, handler) {if (this._events[type]) {this._events[type].splice(this._events[type].indexOf(handler) >>> 0, 1);}}once(type, handler) {let fired = false;function magic() {this.off(type, magic);if (!fired) {fired = true;handler.apply(this, arguments);}}this.on(type, magic);}emit(type) {let payload = [].slice.call(arguments, 1);let array = this._events[type] || [];for (let i = 0; i < array.length; i++) {let handler = this._events[type][i];handler.apply(this, payload);}} }export default EventEmitter; 復(fù)制代碼Vue 中非父子組件組件通信
在 Vue 中不同組件之間通訊,有一種解決方案叫Event Bus,這其實(shí)就是發(fā)布訂閱模式的實(shí)現(xiàn),非常簡(jiǎn)單好用。
// bus.js export default new Vue();// 使用$on監(jiān)聽(tīng) import Bus from '../bus.js';export default {created(){Bus.$on('fulfilled',text=>{this.status = 'fulfilled'})} }// 使用$emit觸發(fā)事件 fetch('/data.json).then((res)=>{return res.json() }).then(()=>{Bus.$emit('fulfilled','已成功') }).catch(()=>{Bus.$emit('resolved','出錯(cuò)'); }) 復(fù)制代碼Vue 事件相關(guān)接口:
emit,off。
使用 Event Bus 進(jìn)行跨組件消息傳遞很棒,但是,當(dāng)事件非常多的時(shí)候,Event Bus 上的事件就會(huì)難以維護(hù),這與使用全局變量一樣一個(gè)道理。變量能局部的,就盡量不要放到全局,同樣 Event Bus 上的事件也是越少越好。
觀察者模式
簡(jiǎn)單創(chuàng)建一個(gè)能夠添加、刪除和提醒觀察者的目標(biāo)。
class Subject {constructor() {this.observer_list = [];}add_observer(obj) {this.observer_list.push(obj);}remove_observer(obj) {for (let i = 0; i < this.observer_list.length; i++) {if (this.observer_list[i] === obj) {this.observer_list.splice(i, 1);}}}notify() {let args = Array.prototype.slice.call(arguments, 0);for (let i = 0; i < this.observer_list.length; i++) {this.observer_list[i].update(args);}} } 復(fù)制代碼代碼會(huì)說(shuō)話(huà),我就不解釋了,實(shí)在是沒(méi)什么好解釋的,如果一定要說(shuō)點(diǎn)什么:我真有點(diǎn)不好意思打原創(chuàng)的標(biāo)。
發(fā)布訂閱模式 與 觀察者模式
有時(shí)候人們會(huì)簡(jiǎn)單的把發(fā)布訂閱模式和觀察者模式混為一談,比如上面的一峰老師,其實(shí)兩者還是有些區(qū)別的。至少它們的英文名稱(chēng)不同:Observer vs Pub-Sub。
《Learning JavaScript Design Patterns》一書(shū)這樣說(shuō):
“While the Observer pattern is useful to be aware of, quite often in the JavaScript world, we’ll find it commonly implemented using a variation known as the Publish/Subscribe pattern.” 雖然 Observer 模式非常有用,但是在 JavaScript 的世界中,它更多的以一種被稱(chēng)為發(fā)布/訂閱模式的變種來(lái)實(shí)現(xiàn)
由上可以理解為,發(fā)布/訂閱模式是觀察者模式的一種變形,兩者區(qū)別在于,發(fā)布/訂閱模式在觀察者模式的基礎(chǔ)上,在目標(biāo)和觀察者之間增加一個(gè)調(diào)度中心。
觀察者模式是由具體目標(biāo)調(diào)度,比如當(dāng)事件觸發(fā),Subject 就會(huì)去調(diào)用觀察者的方法,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴(lài)的。
發(fā)布/訂閱模式由統(tǒng)一調(diào)度中心調(diào)用,因此發(fā)布者和訂閱者不需要知道對(duì)方的存在。
詳細(xì)內(nèi)容,感興趣的同學(xué)可以看下面這本書(shū):
總結(jié)
以上是生活随笔為你收集整理的【设计模式】前端必懂EventEmitter的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 聚能聊每周精选 第二十三期
- 下一篇: SQLServer 清空某个库所有表