发布订阅模式
- 发布订阅模式分为:订阅者、发布者、信号中心
- 假定,存在一个 “信号中心”,某个任务执行完成,就向信号中心 “发布” (publish) 一个信号;
- 其他任务可以向信号中心 “订阅” (subscribe) 这个信号,从而知道什么时候自己可以开始执行;
- 订阅和发布没有任明显关系,主要靠信号中心关联;
- 这就叫做 “发布/订阅式” (publish-subscribepattern);
应用场景
-
Vue的自定义事件
// Vue 自定义事件 let vm = new Vue() // { 'click': [fn1, fn2], 'change': [fn] } // 注册事件(订阅消息) vm.$on('dataChange', () => { console.log('dataChange') }) vm.$on('dataChange', () => { console.log('dataChange1') }) // 触发事件(发布消息) vm.$emit('dataChange')
-
兄弟组件通信过程(也叫事件总线)
// 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 自定义事件的实现
// 事件触发器
class EventEmitter {
constructor() {
// 信号中心:{ 'click': [fn1, fn2], 'change': [fn] }
this.subs = Object.create(null) // 创建一个没有原型属性的 object,性能要好一点
}
// 订阅通知
$on(eventType, handler) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
// 发布通知
$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')
观察者模式
-
观察者(订阅者) – Watcher
- update():当事件发生时,具体要做的事情;
-
目标(发布者) – Dep
- subs 数组:收集所有的观察者;
- addSub():添加观察者;
- notify():当事件发生,调用所有观察者的 update() 方法;
-
实现代码
// 目标(被观察者) class Dep { constructor() { // 收集所有的观察者 watcher this.subs = [] } // 添加订阅者 addSub(watcher) { if (watcher&& watcher.update) { this.subs.push(watcher) } } // 发布通知 notify() { this.subs.forEach(watcher => { watcher.update() }) } } // 观察者 class Watcher { update() { console.log('update') } } // 测试 let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify()
观察者 VS 发布订阅
-
观察者模式:是由具体目标调度,比如当事件触发,Dep 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的;
-
发布/订阅模式:由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在,这样一方面实现了解耦,还可以实现更细粒度的一些控制,比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的;还可以做一些节流操作;
-
对比图:
vue2.x✍️ slot 插槽
上一篇