前言
作为一名软件开发人员,了解常用的设计模式应该算是我们必备的技能之一了。如果你在编程中可以得心应手的使用这些,那你的代码肯定是满足了健壮性,可读性,易维护的范畴之内的。本章我们一起来了解下前端开发中常用的设计模式发布 订阅。
发布订阅者模式是什么
说道发布订阅者模式,那不得不提的就是观察者模式,让我们先来对比下他们基础特性(摘自维基百科):
- 观察者模式:在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
- 发布订阅者模式:在软件架构中,发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。
从他们对比的特征中不难发现这两者有很多相似点:都是一对多的关系,(一个观察者对象对应多个观察者、一个发布对应多个订阅者。)都能实现数据的绑定。他们本质上的区别是发布订阅会引入Event Channel(订阅器) 来将发布的信息分发到订阅者 (所以发布订阅可以异 步处理订阅的内容)。
发布订阅者-vue中的双向绑定
DOM ======> 数据 或 数据 ======> DOM *从图中我们可以清楚的了解到 vue初始化时候会加载observer类,这个类是为了 实现数据劫持,通过Object.propertyDefine为其添加set和get属性,并且将这些值添加到订阅器中(deb)。
//实现observer数据劫持 export class Observer { ... //遍历所有属性 walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } } //给属性加上get set export function defineReactive ( ... ) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { ... if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { ... dep.notify() } }) } //实现订阅者
- 我们再来看看订阅器中如何实现的,我们订阅器中需要的主要功能收集和发布。 收集observer中劫持到到数据关联对应的数据和订阅者,通知对应的订阅者订阅到数据改变。
//实现订阅器 export default class Dep { //收集 addSub (sub: Watcher) { ... } // depend () { if (Dep.target) { Dep.target.addDep(this) } } //通知 notify () { ... } }
*最后我们来了解下订阅者(watcher)接受到订阅器到通知后所做到事情
export default class Watcher { // ... /** * Subscriber interface. * Will be called when a dependency changes. */ // 还记得Dep.notify 调用到update update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } } /** * Scheduler job interface. * Will be called by the scheduler. */ //这个方法并不是实例化Watcher的时候执行的,而是监听的变量变化的时候才执行的 run () { ... } } }
发布订阅的优缺点以及疑问
- 优点:发布者和订阅者是解耦的,符合了软件设计高内聚,低耦合,只要引入订阅发布模式的事件中心,无论在何处都可以发布订阅。同时订阅发布者相互之间不影响。
- 缺点:1.使用不当就会造成数据流混乱,导致代码不好维护。2.订阅发布模式需要维护事件列队,订阅的事件越多,内存消耗越大
- 在vue中数据改变如何通知视图变化的?
解析指令 ===> 数据劫持 ===> 触发更新(通过watcher中的set) - 在vue到双向绑定中订阅器如何分发数据到对应到订阅者?
总结
前期也写过总结vue源码的相关文章,那我为何此时还要再写一篇此类文章?因为随着深入对vue的使用,渐渐发现掌握vue双向绑定的设计结构是如此重要,vue的此种设计不仅可以应用于软件开发中的多种场景,而且是我们熟练掌握vue这个开发框架的必经之路。