一、前言
发布订阅模式,基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订阅主题,被激活事件的对象(称为publisher)通过发布主题事件的方式被通知。
就和用户订阅微信公众号道理一样,一个公众号可以被多个用户同时订阅,当公众号有新增内容时候,只要发布就好了,用户就能接收到最新的内容。
js中的事件监听机制就是一种观察者模式。
二、和观察者模式的区别
观察者模式:一个对象(称为subject)维持一系列依赖于它的对象(称为observer),将有关状态的任何变更自动通知给它们(观察者)。
1、Observer模式要求观察者必须订阅内容改变的事件,定义了一个一对多的依赖关系;
2、Publish/Subscribe模式使用了一个主题/事件通道,这个通道介于订阅着与发布者之间;
3、观察者模式里面观察者「被迫」执行内容改变事件(subject内容事件);发布/订阅模式中,订阅着可以自定义事件处理程序;
4、观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合读底
三、实现一个简单demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | function Public(){ //存放订阅者信息 this .subscribers = []; //添加订阅者 this .addSubscriber = function (subscriber){ //保证一个订阅者只能订阅一次 let isExist = this .subscribers.some( function (item){ return item == subscriber; }) if (!isExist){ this .subscribers.push(subscriber); } return this ; } //发布消息 this .deliver = function (data){ this .subscribers.forEach( function (fn){ fn(data) }) return this ; } } ------------------------------------------------------ //订阅者 let a = function (data){ console.log(`订阅者a收到订阅信息:${data}`) } let b = function (data){ console.log(`订阅者b收到订阅信息:${data}`) } let c = function (data){ console.log(`订阅者c收到订阅信息:${data}`) } //初始化 let publisher = new Public(); //添加订阅者 publisher.addSubscriber(a); publisher.addSubscriber(b).addSubscriber(c); //公众号发布消息 publisher.deliver( '这是公众号推送的第1条新信息!' ); publisher.deliver( '这是公众号推送的第2条新信息!' ).deliver( '这是公众号推送的第3条新信息!' ); |
看看node中的EventEmitter
对象触发一个事件时,所有绑定在该事件上的函数都被同步地调用。 是不是很熟悉的感觉~~
1 2 3 4 5 6 7 8 9 | const EventEmitter = require( 'events' ); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on( 'event' , () => { console.log( '触发了一个事件!' ); }); myEmitter.emit( 'event' ); |
2、可以看到观察者模式有如下优点
a、每一个订阅者都是相互独立的只和发布者有关系,与发布者是一对多的关系,也可以是一对一的关系。
b、每一个订阅者可以根据自己的需求来调用,而不影响其它订阅者
c、与第一种方式相比,第二种方式的代码可读性、可维护性强;
四、完整的demo
上面的还是比较简答。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | function Public() { this .handlers = {}; } Public.prototype = { // 订阅事件 on: function (eventType, handler){ var self = this ; if (!(eventType in self.handlers)) { self.handlers[eventType] = []; } self.handlers[eventType].push(handler); return this ; }, // 触发事件(发布事件) emit: function (eventType){ var self = this ; var handlerArgs = Array.prototype.slice.call(arguments,1); for ( var i = 0; i < self.handlers[eventType].length; i++) { self.handlers[eventType][i].apply(self,handlerArgs); } return self; }, // 删除订阅事件 off: function (eventType, handler){ var currentEvent = this .handlers[eventType]; var len = 0; if (currentEvent) { len = currentEvent.length; for ( var i = len - 1; i >= 0; i--){ if (currentEvent[i] === handler){ currentEvent.splice(i, 1); } } } return this ; } }; var Publisher = new Public(); //订阅事件a Publisher.on( 'a' , function (data){ console.log(1 + data); }); Publisher.on( 'a' , function (data){ console.log(2 + data); }); //触发事件a Publisher.emit( 'a' , '我是第1次调用的参数' ); Publisher.emit( 'a' , '我是第2次调用的参数' ); |
五、vue中的作用
Vue会遍历实例的data属性,把每一个data都设置为访问器,然后在该属性的getter函数中将其设为watcher,在setter中向其他watcher发布改变的消息。
这样,配合发布/订阅模式,改变其中的一个值,会发布消息,所有的watcher会更新自己,这些watcher也就是绑定在dom中的显示信息。
从而达到改变浏dom,在浏览器中实时变化的效果,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | //遍历传入实例的data对象的属性,将其设置为Vue对象的访问器属性 function observe(obj,vm){ Object.keys(obj).forEach( function (key){ defineReactive(vm,key,obj[key]); }); } //设置为访问器属性,并在其getter和setter函数中,使用订阅发布模式。互相监听。 function defineReactive(obj,key,val){ //这里用到了观察者(订阅/发布)模式,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。 //实例化一个主题对象,对象中有空的观察者列表 var dep = new Dep(); //将data的每一个属性都设置为Vue对象的访问器属性,属性名和data中相同 //所以每次修改Vue.data的时候,都会调用下边的get和set方法。然后会监听v-model的input事件,当改变了input的值,就相应的改变Vue.data的数据,然后触发这里的set方法 Object.defineProperty(obj,key,{ get: function (){ //Dep.target指针指向watcher,增加订阅者watcher到主体对象Dep if (Dep.target){ dep.addSub(Dep.target); } return val; }, set: function (newVal){ if (newVal === val){ return } val = newVal; //console.log(val); //给订阅者列表中的watchers发出通知 dep.notify(); } }); } //主题对象Dep构造函数 function Dep(){ this .subs = []; } //Dep有两个方法,增加订阅者 和 发布消息 Dep.prototype = { addSub: function (sub){ this .subs.push(sub); }, notify: function (){ this .subs.forEach( function (sub){ sub.update(); }); } } |
原文 https://www.cnblogs.com/leaf930814/p/9014200.html