>[success] 发布订阅 和 观察者 [参看关于发布订阅 和观察者我在其他专栏的总结链接](https://www.kancloud.cn/cyyspring/more/1351742) >[info] ## 发布订阅 ~~~ 1.发布订阅模式的构成三部分,'订阅者'、'发布者'、'信号中心' 2.假定 存在一个'信号中心',某个任务执行完成,就向信号中心'发布'(publish)一个信号, 其他任务可以向信号中心'订阅'(subscribe)这个信号,从而知道什么时候自己可以开始执行。 这就叫做'发布/订阅模式'(publish-subscribe pattern) 3.简单的理解'信号中心' 其实有两个核心功能'注册事件','触发事件'。订阅者通过在信号中心'注册事件', 发布者通过发布信号触发信号中心的'触发事件'。让订阅者接收到触发信息 ~~~ >[danger] ##### vue 中对发布订阅的应用 ~~~ 1.vue中自定义事件就是利用了'发布订阅模式' // 订阅中心 const vm = new Vue({}) // 注册事件 vm.$on('dataChage', (msg) => { console.log(msg) // 测试数据 }) // 触发事件 vm.$emit('dataChage', '测试数据') 2.下面案例把调用信号中心注册事件的叫'订阅者',使用信号中心触发事件叫'发布者' // eventBus.js // 事件中心 let eventHub = new Vue() // ComponentA.vue // 发布者 addTodu: function () { // 发布消息(事件) 触发事件 eventHub.$emit('add-todo', { text: this.newTodoText }) this.newTodoText = '' } // ComponentB.vue // 订阅者 created: function () { // 注册事件 eventHub.$on('add-todo', this.addTodo) } ~~~ >[danger] ##### 模拟 Vue 自定义事件的实现 ~~~ // 事件中心 class EventEmitter { constructor() { // { 'click': [fn1, fn2], 'change': [fn] } 键为事件名称 值为存储事件处理函数的数组 this.subs = Object.create(null) } // 注册事件,需要事件名和事件方法因此需要两个参数 $on(eventType, handler) { this.subs[eventType] = this.subs[eventType] || [] this.subs[eventType].push(handler) } // 触发事件,触发事件是在注册事件中标记的 // 因此第一个参数触发事件名 第二个参数是传递的参数 $emit(eventType, ...params) { this.subs[eventType] && this.subs[eventType].forEach(handler => { handler(...params) }) } } let em = new EventEmitter() em.$on('click', (val) => { console.log('click1', val) }) em.$on('click', (val) => { console.log('click2', val) }) em.$emit('click', '测试') // 打印结果 click1 测试 click2 测试 ~~~ >[info] ## 观察者 ~~~ 1.观察者(订阅者) - Watcher 1.1.update(): 当事件发生时,具体要做的事情 2.目标(发布者) - Dep 知道观察者的存在 2.1.subs 数组: 存储所有的观察者 2.2.addSub(): 添加观察者 2.3.notify(): 当事件发生,调用所有观察者的 update() 方法 3.观察者和发布订阅的最大区别'没有事件中心',观察者模式是通过发布者进行进行记录观察者和调用观察者 ~~~ >[danger] ##### 观察者例子代码 ~~~ // 发布者 class Dep { constructor() { this.subs = [] } // 记录观察者 addSub(sub) { if (sub && sub.update) { this.subs.push(sub) } } // 发布通知 notify() { this.subs.forEach(sub => { sub.update() }) } } // 观察者具备updata 方法 // 订阅者-观察者 class Watcher { update() { console.log('update') } } // 测试 let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify() ~~~ >[info] ## 二者的区别 ~~~ 1.观察者模式是由具体目标调度,比如当事件触发,Dep就会去调用观察者方法, 所以观察者模式的订阅者与发布者之间存在依赖关系 2.发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方存在, 隔离两者且减少两者之间的关系 3.通过发布订阅 和 观察者 中二者对订阅者的存储结构来看,发布定订阅由于大家共享一个事件中心 因此为了区分每个订阅者想要订阅的类型因此采用的是对象形式'{evnet:[....],evnet2:[....]}' 观察者只是为自己的那一类订阅者服务因此他的形式是数组'[]' 简单的说,发布定订阅是所有订阅者的集合 并且利用key 进行分类,而观察者是一类订阅者,如果这类订阅者不是此类观察者,需要创建新的观察 者对这类订阅者进行订阅 和 发布 ~~~ ![](https://img.kancloud.cn/9f/03/9f0310c07da325e64a690b42326b19d6_768x546.png)