拿什么拯救非父子组件通信,我的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发布订阅模式中看语言间的“通用思想”
发布了195 篇原创文章 · 获赞 391 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_43624878/article/details/103536296