发布订阅模式:订阅者,发布者,信号中心
我们假定,存在一个“信号中心”,某个任务执行完成,就向信号中心"发布"(publish)一个信号,其它任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)
家长向学生所在的班级订阅了获取学生考试成绩的事件,当老师公布学生的成绩后,就会自动通知学生的家长。
在整个案例中,学生所在的班级为信号中心,老师为发布者,家长为订阅者
Vue 的自定义事件就是基于发布订阅模式来实现的。
下面通过Vue中兄弟组件通信过程,来理解发布订阅模式
// eventBus.js
// 事件中心
let eventHub=new Vue()
//ComponentA.vue
addTodo:function(){
//发布消息(事件)
eventHub.$emit('add-todo',{text:this.newTodoText})
this.newTodoText=''
}
//ComponentB.vue
//订阅者
created:function(){
//订阅消息(事件)
eventHub.$on('add-todo',this.addTodo)
}
通过以上代码,我们可以理解发布订阅模式中的核心概念。
下面我们模拟Vue中的自定义事件的实现
下面我们先来做一个基本的分析:
先来看如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue 自定义事件</title>
</head>
<body>
<script src="./js/vue.js"></script>
<script>
//Vue自定义事件
let vm = new Vue();
//注册事件(订阅消息)
vm.$on("dataChange", () => {
console.log("dataChange");
});
vm.$on("dataChange", () => {
console.log("dataChange");
});
//触发事件(发布消息)
vm.$emit("dataChange");
</script>
</body>
</html>
通过上面的代码,我们可以看到$on实现事件的注册,而且可以注册多个事件,那么我们可以推测在其内部有一个对象来存储注册的事件,对象的格式为:
{'click':[fn1,fn2],'change':[fn]}
以上格式说明了,我们注册了两个事件,分别为click与change.
下面我们根据以上的分析过程,来模拟实现自定义事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>发布订阅模式</title>
</head>
<body>
<script>
class EventEmitter {
constructor() {
// {'click':[fn1,fn2],'change':[fn]}
// 存储事件与处理函数的对应关系
this.subs = {};
}
//注册事件
//第一个参数为事件名称
// 第二个参数为处理函数
// 将对应的处理函数添加到subs对象中
$on(eventType, fn) {
//判断对应的eventType是否有相应的处理函数,如果有,直接添加到数组中,如果没有返回一个空数组。
if (!this.subs[eventType]) {
this.subs[eventType] = [];
}
this.subs[eventType].push(fn);
}
//触发事件
$emit(eventType) {
if (this.subs[eventType]) {
this.subs[eventType].forEach((handler) => {
handler();
});
}
}
}
//测试代码
let em = new EventEmitter();
em.$on("click", () => {
console.log("click1");
});
em.$on("click", () => {
console.log("click2");
});
em.$emit("click");
</script>
</body>
</html>
\