拿什麼拯救非父子組件通信,我的vue

一道面試題
最近在看《JavaScript設計模式與開發實踐》中的【發佈訂閱模式和觀察者模式】。我不禁想起了上半年面試的時候一個面試官問我的問題:“你在實際項目中是如何處理非父子組件通信的?”
我回答的是:“大型項目的話一般都會用vuex,在一些小場景裏會用EventEmitter”。
沒想到面試官接着來了一句:“那你能手寫代碼實現一個簡單的EventEmitter嗎?”

手寫WventEmitter

我想了一下,這主要是使用了emit發事件,用on去監聽,還有off銷燬事件監聽,once實現單次事件處理…等等。考慮到時間緊張,我就只實現了收、發事件,移除監聽的功能,有驚無險。。。

其實細想,這個和vue中內置實現的$emit$on是差不多的
且看下面代碼:

class EventEmitter{
	constructor() {
	    // 維護事件及監聽者
		this.listenners={}
	}
	/**
	 * 註冊事件監聽者
	 * @param {Object} type 事件類型
	 * @param {Object} cb 回調函數
	 */
	on(type,cb){
		if(!this.listenners[type]){
			this.listenners[type]=[]
		}
		this.listenners[type].push(cb)
	}
	/**
	 * 發佈事件
	 * @param {String} type 事件類型
	 * @param {Function} cb 回調函數  
	 */
	emit(type,...args){
		if(this.listenners[type]){
			this.listenners[type].forEach(cb=>{
				cb(...args)
			})
		}
	}
	/**
	 * 移除某個事件的一個監聽者
	 * @param {Object} type 事件類型
	 * @param {Object} cb 回調函數
	 */
	off(type,cb){
		if(this.listenners[type]){
			const targetIndex=this.listenners[type].findIndex(item=>item===cb)
			if(targetIndex!==-1){
				this.listenners[type].splice(targetIndex,1)
			}
			if(this.listenners[type].length===0){
				delete this.listenners[type]
			}
		}
	}
	/**
	 * 移除某個事件的所有監聽者
	 * @param {Object} type 事件類型
	 */
	offAll(type){
		if(this.listenners[type]){
			delete this.listenners[type]
		}
	}
}

有了這個自己實現的簡單版本的EventEmitter,我們就不用依賴第三方庫了!

const mxc=new EventEmitter()

mxc.on('mxc',function(address,food){console.log('我餓了,我們取${address}吃${food}!')})
mxc.emit('mxc','南門','小火鍋')

對了,這和Vue的:

const ee = new Vue();
ee.$on('chifan', function(address, food) { console.log(`吃飯了,我們去${address}${food}!`) })
ee.$emit('chifan', '三食堂', '鐵板飯')

可是有異曲同工之妙!

再往下考慮就會發現,EventEmitter就是一個典型的發佈訂閱模式,實現了事件調度中心。
發佈訂閱模式中,包含發佈者、事件調度中心、訂閱者三個角色,我們剛剛實現的EventEmitter的一個實例:mxc,就是一個事件調度中心,發佈者和訂閱者之間是互不關心的,它們是鬆散耦合的。它們關注事件本身。


若有不懂之處,筆者還準備瞭如下文章用以解惑:

  1. vue組件的通信:自定義實現(已廢棄的)dispatch和broadcast方法
  2. 從vue組件傳值和JS發佈訂閱模式中看語言間的“通用思想”
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章