您现在的位置: 万盛学电脑网 >> 程序编程 >> 脚本专题 >> javascript >> 正文

详解JavaScript中的客户端消息框架设计原理

作者:佚名    责任编辑:admin    更新时间:2022-06-22

   这篇文章主要介绍了详解JavaScript中的客户端消息框架设计原理,包括客户端和服务器端的通信等方面的内容,需要的朋友可以参考下

  哇——是个危险的题目,对吗?我们对于什么是本质的理解当然会随着我们对要解决问题的理解而变化。因此我不会说谎——一年前我所理解的本质很不幸并不完整,因为我确信我将要写的已经快伴随我有6个月之久。所以,这篇文章是我在发现JavaScript中成功的运用客户端消息模式的一些关键要点时的一个掠影。

  1.) 理解中介者与观察者的区别

  大多数人在描述任何事件/消息机制的时候喜欢套用“发布者/订阅者”(pub/sub)——但我认为这个术语不能很好的与抽象建立联系。当然,从根本上说,一些东西订阅了另一些东西发布的事件。但是发布者与订阅者在何等层次上封装在一起有可能使一个好的模式变得暗淡无光。那么,区别在什么地方呢?

  观察者

  观察者模式包括了被一个或多个观察者所观察的某个对象。典型的,该对象记录下所有观察者的痕迹,通常是用一个list来存储观察者注册的回调方法,这些是观察者为了接收通知而订阅的。 注意: (哦,双关语,我有多爱他们啊)(译者注:Observe 观察、注意)

  ?

1 2 3 4 5 6 7 var observer = { listen : function() { console.log("Yay for more cliché examples..."); } }; var elem = document.getElementById("cliche"); elem.addEventListener("click", observer.listen);

  一些需要注意的事情是:

  我们必须获得对此对象的直接引用

  此对象必须保持一些内部的状态,保存观察者的回调痕迹

  有时侦听者不会利用由此对象返回的任何参数,理论上来说,有可能有 0-n*个参数 (更多是取决于以后会变得多有趣)

  * n事实上不是无限的,但为了讨论的目的,它指我们永远也达不到的极限

  中介者

  中介者模式在一个对象与一个观察者之间引入了一个“第三方”——有效的将二者解耦而且将他们之间如何通信封装起来。一个中介者的API可能像“发布”、“订阅”、“取消订阅”一样简单,或者某个领域范围内的实现可能被提供用来隐藏这些方法于某些更有意义的语义之中。大多数我用过的服务器端的实现更倾向于领域范围而不是更简单,但是并没有对一个通用的中介者有任何规则限制!并不罕见,有种想法认为一个通用的中介者是一种信息经纪人。无论何种情形,结果都一样——特定对象与观察者之间不再互相直接知晓:

  ?

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 // It's fun to be naive! var mediator = { _subs: {}, // a real subscribe would at least check to make sure the // same callback instance wasn't registered 2x. // Sheesh, where did they find this guy?! subscribe: function(topic, callback) { this._subs[topic] = this._subs[topic] || []; this._subs[topic].push(callback); }, // lolwut? No ability to pass function context? :-) publish : function(topic, data) { var subs = this._subs[topic] || []; subs.forEach(function(cb) { cb(data); }); } } var FatherTime = function(med) { this.mediator = med; }; FatherTime.prototype.wakeyWakey = function() { this.mediator.publish("alarm.clock", { time: "06:00 AM", canSnooze: "heck-no-get-up-lazy-bum" }); } var Developer = function(mediator) { this.mediator = mediator; this.mediator.subscribe("alarm.clock", this.pleaseGodNo); }; Developer.prototype.pleaseGodNo = function(data) { alert("ZOMG, it's " + data.time + ". Please just make it stop."); } var fatherTime = new FatherTime(mediator); var developer = new Developer(mediator); fatherTime.wakeyWakey();

  你可能会想,除了特别纯粹的中介者实现,特定对象不再负有保存订阅者列表的责任,而且“时光老人”(FatherTime)与“开发者”(Developer)实例永远没法真正互相知道。他们只是共享了一个信息——将如我们今后所见,这是一个很重要的合约。 “很好,Jim。这对我而言仍然是发布者/订阅者,那么重点呢?我选择某个方向真的会有区别吗?”哦,继续吧,亲爱的读者们,继续吧。

  2.) 了解什么时候使用中介者和观察者

  使用本地的观察者和中介者,即写在组件当中的,而中介者看起来又像远程的组件间通信。不管怎样。我对待这种情况的原则虽然是——tl;dr(too long; don't read)(太长,不读了)。但无论如何,反正串联在一起最好。

  要我简捷地说真是麻烦,就像把几个月来的细致体验压缩到装不下140个字的沟里。现实中回答这个问题肯定不简洁。所以有一个长版本的解释:

  观察者除了关心数据映射之外还有必要引用别的项目吗?例如Backbone.View视图有各种理由直接引用它的模型。这是非常自然的关系,视图不仅要在模型改变时进行渲染,还需要调用模型的事件处理。如果段首的问题答案是”yes“,那观察者就是有意义的。

  如果观察者和观察对象的关系仅仅是依赖数据,那我愿意使用中介pub/sub方式。两个Backbone.View视图或模型之间的通信,用观察者是合适的。比如控制导航菜单的视图发出的信息,是面包屑(breadcrumb)挂件需要的(响应当前的层级)。挂件不需要引用导航视图,它只需要导航视图提供信息。更关键的,导航视图也许不是唯一的信息来源,别的视图可能也可以提供。此时,中介pub/sub模式是最理想的——而且自身扩展性良好。

  看起来这样又好又全面,但是其实还有一个露点:如果我给对象定义一个本地事件,既想要观察者直接调用,又可以被订阅者间接访问到,怎么办?这就是我为什么说要串联在一起:你推送或者桥接本地事件到消息组去吧。需要些更多代码?很有可能——但是总比你把观察对象传递给所有观察者,一直紧耦合下去的情况好。然后,我们可以很好地继续以下两点...

  3.) 选择性的“提交”本地事件到总线

  最开始我几乎只用观察者模式来在JavaScript中触发事件。这是我们一次又一次遇到的模式,但更流行的客户端辅助库行为方式根本上来说是混合中介者的,给我们提供了就像它们是观察者模式的API。我最初写postal.js的时候,开始走进“为所有事物搭中介”的阶段。在我写的原型与构造函数中,分布各处的发布与订阅的调用并不罕见。当我从这个改变中自然的解耦受益时,非基础的代码开始似乎充满了相关于基础的部分。构造函数到处都要带上一个通道,订阅被当作新实例的一部分被创建,原型方法直接发布一个数值到总线(甚至本地的订阅者都不能直接的而必须监听总线以获得信息)。将这些明显关于总线的东西纳入app的这些部分,开始像是代码的味道。代码的“叙述”似乎总是被打断,如“噢,将这个向所有订阅者发布出去”,“等等!等等!监听这个通道那个事情。好,现在继续吧”。我的测试忽然开始需要依赖总线来做低层次的单元测试。而这感觉有点不对劲。

  钟摆摆动的指向了中间,我认识到我应该保持一个“本地API”,并且在需要的时候通过一个中介者为应用扩展其可以触及的数据。 例如,我的backbone视图与模型,仍然用普通的Backbome.Events行为来给本地观察者发送事件(就是说,模型的事件被它相应的视图所观察)。当app的其它部分需要知道模型的变化时,我开始通过这些行将本地事件与总线桥接起来:

  ?

1 2 3 4 5 6 7 8 9 10 1