Js 框架模塊

11月中旬在倫敦舉行的jQuery Summit頂級大會上有個session講的是大型JavaScript應(yīng)用程序架構(gòu)兴枯,看完P(guān)PT以后覺得甚是不錯涮俄,于是整理一下發(fā)給大家共勉。

PDF版的PPT下載地址:http://www.slideshare.net/jibyjohnc/jqquerysummit-largescale-javascript-application-architecture

注:在整理的過程中,發(fā)現(xiàn)作者有些思想是返來復(fù)去地說,所以刪減了一部分运怖,如果你的英文良好,請直接閱讀英文的PPT夏伊。

以下是本文的主要章節(jié):

1. 什么叫“JavaScript大型程序”摇展?

2. 顧當前的程序架構(gòu)

3. 長遠考慮

4. 頭腦風暴

5. 建議的架構(gòu)

5.1 設(shè)計模式

5.1.1 模塊論

5.1.1.1 綜述

5.1.1.2 Module模式

5.1.1.3 對象自面量

5.1.1.4 CommonJS模塊

5.1.2 Facade模式

5.1.3 Mediator模式

5.2 應(yīng)用到你的架構(gòu)

5.2.1 Facade - 核心抽象

5.2.2 Mediator - 程序核心

5.2.3 緊密聯(lián)合運作起來

6. 發(fā)布Pub/訂閱Sub的延伸:自動注冊事件

7. Q & A

8. 致謝

什么叫“JavaScript大型程序”?

在我們開始之前溺忧,我們來定義一下什么叫大型JavaScript站點咏连,很多有經(jīng)驗的JS開發(fā)高手也都被challenge住了,有人說超過10萬行JavaScript代碼才算大型鲁森,也有人說JavaScript代碼要超過1MB大小才算祟滴,其實2者都不能算對,因為不能安裝代碼量的多少來衡量歌溉,很多瑣碎的JS代碼很容易超過10萬行的垄懂。

我對“大”的定義如下,雖然可能不太對,但是應(yīng)該是比較接近了:

我個人認為埠偿,大型JavaScript程序應(yīng)該是非常重要并且融入了很多卓越開發(fā)人員努力,對重量級數(shù)據(jù)進行處理并且展示給瀏覽器的程序榜晦。

回顧當前的程序架構(gòu)

我不能強調(diào)說這個問題有多重要冠蒋,很多有經(jīng)驗的開發(fā)人員經(jīng)常說:“現(xiàn)有的創(chuàng)意和設(shè)計模式在我上一個中型項目上運行得非常好,所以在稍微大型點的程序里再次使用乾胶,應(yīng)該沒問題抖剿,對吧?”,在一定程序上是沒錯的识窿,但別忘記了斩郎,既然是大型程序,通常就應(yīng)該有大的Concerns需要分解關(guān)注喻频,我簡短解釋一下要花時間來review當前運行了很久的程序架構(gòu)缩宜。大多數(shù)情況下,當前的JavaScript程序架構(gòu)應(yīng)該是如下這個樣子的(注意甥温,是JS架構(gòu)锻煌,不是大家常說的ASP.NET MVC):

custom widgets

models

views

controllers

templates

libraries/toolkits

an application core.

你可能還會將程序單獨封裝成多個modules,或者使用其他的設(shè)計模式姻蚓,這很好宋梧,但是如果這些結(jié)構(gòu)完全代表你的架構(gòu)的話,就可能會有一些潛在的問題狰挡,我們來看看幾個重要的點:

1.你架構(gòu)里的東西捂龄,有多少可以立即拿出來重用?

有沒有一些單獨的module不依賴別的代碼加叁?是自包含么倦沧?如果我到你們正在使用的代碼庫上去隨即挑選一些模塊module代碼,然后放在一個新頁面它匕,是否能立即就能使用刀脏?你可能會說原理通就可以了,我建議你長久打算一下超凳,如果你的公司之前開發(fā)很多重要的程序愈污,突然有一天有人說碟绑,這個項目里的聊天模塊不錯难裆,我們拿出來放在另外一個項目里吧摸屠,你能直接拿過來不修改代碼就能使用么始锚?

2.系統(tǒng)里有多少模塊module需要依賴其他模塊蓖乘?

系統(tǒng)的各個模塊是不是都很緊耦合授瘦?在我將這個問題作為concern之前二拐,我先解釋一下茂翔,不是說所有的模塊都絕對不能有任何依賴,比如一個細粒度的功能可能是從base功能擴展來的涧尿,我的問題和這種情況不一樣系奉,我說的是不同功能模塊之前的依賴,理論上姑廉,所有的不同功能模塊都不應(yīng)該有太多的依賴缺亮。

3.如果你程序的某一部分出錯了,其他部分是否能夠依然工作桥言?

如果你構(gòu)建一個和Gmail差不多的程序萌踱,你可以發(fā)現(xiàn)Gmail里很多模塊都是動態(tài)加載的,比如聊天chat模塊号阿,在初始化頁面的時候是不加載的并鸵,而且就算加載以后出錯了,頁面的其他部分也能正常使用扔涧。

4.你的各個模塊Module能很簡單的進行測試么园担?

你的每一個模塊都有可能用在數(shù)百萬用戶的大型站點上,甚至多個站點都使用它枯夜,所以你的模塊需要能經(jīng)得住測試粉铐,也就是說,不管是在架構(gòu)內(nèi)部還是架構(gòu)外部卤档,都應(yīng)該能很簡單的去測試蝙泼,包括大部分的斷言在不同的環(huán)境下都能夠通過。

長遠考慮

架構(gòu)大型程序的時候劝枣,最重要的是要有前瞻性汤踏,不能只考慮一個月或者一年以后的情況,要考慮更長久的情況下舔腾,有什么改變的可能性溪胶?開發(fā)人員經(jīng)常將DOM操作的代碼和程序綁定得太緊,盡管有時候已經(jīng)封裝單獨的邏輯到不同的模塊里了稳诚,想想一下哗脖,長久以后,為什么不是很好扳还。

我的一個同事曾經(jīng)說過才避,一個精確的架構(gòu)可能不適合未來的情景,有時候是正確的氨距,但是當你需要該做的話桑逝,你所付出的money那可是相當?shù)囟嗯丁1热缜稳茫憧赡芤驗槟承┬阅芾愣簦踩缦荆O(shè)計的原因需要在Dojo, jQuery, Zepto, YUI之間需要選擇替換,這時候就有問題了寡喝,大部分模塊都有依賴糙俗,需要錢呀,需要時間啊预鬓,需要人呀巧骚,對不?

對于一些小型站點沒事珊皿,但是大型站點確實需要提供一個更加靈活的機制,而不去擔心各個模塊之間的各種問題巨税,這既然節(jié)約錢蟋定,又能節(jié)約時間。

總結(jié)一下草添,現(xiàn)在你能確定你能不重寫整個程序就能替換一些類庫么驶兜?如果不能,那估計我們下面要講的內(nèi)容远寸,就比較適合你了抄淑。

很多有經(jīng)驗的JavaScript開發(fā)者給出了一些關(guān)鍵的notes:

JavaScriptMVC的作者Justin Meyer說

構(gòu)建大型程序最大的秘密就是從來不構(gòu)建大型程序,而是將程序分解成各個小的模塊去做驰后,讓每個小模塊都可測試肆资,可size化,然后集成到程序里灶芝。

High-performance JavaScript websites作者Nicholas,Zakas

"The key is to acknowledge from the start that you have no idea how this will grow. When you accept that you don't know everything, you begin to design the system defensively. You identify the key areas that may change, which often is very easy when you put a little bit of time into it. For instance, you should expect that any part of the app that communicates with another system will likely change, so you need to abstract that away." -

一大堆文字問題郑原,太麻煩了,總結(jié)一句就是夜涕,一切皆可變犯犁,所以要抽象。

jQuery Fundamentals作者Rebecca Murphey:

各個模塊之間聯(lián)系的越密切女器,重用性越小酸役,改變起來困難越大。

以上這些重要觀點驾胆,是構(gòu)建架構(gòu)的核心要素涣澡,我們需要時刻銘記。

頭腦風暴

我們來頭腦風暴一下丧诺,我們需要一個松耦合的架構(gòu)暑塑,各模塊之間沒有依賴,各個模塊和程序進行通信锅必,然后中間層接管和處理反饋相應(yīng)的消息事格。

例如惕艳,我們?nèi)绻幸粋€JavaScript構(gòu)建在線面包店程序,一個模塊發(fā)出了一個信息可能是“有42個圓面包需要派件”驹愚。我們使用不同的layer層來處理模塊發(fā)來的消息远搪,做到如下:

模塊不直接訪問程序核心

模塊不直接調(diào)用或影響其它的模塊

這將防止我們因為某個模塊出錯,而導(dǎo)致所有的模塊出錯逢捺。

另外一個問題是安全谁鳍,真實的情況是,大多數(shù)人都不認為內(nèi)部安全是個問題劫瞳,我們自己心里說倘潜,程序是我自己構(gòu)建的,我知道哪些是公開的那些私有的志于,安全沒問題涮因,但你有沒有辦法去定義哪個模塊才能權(quán)限訪問程序核心?例如伺绽,有一個chat聊天模塊养泡,我不想讓他調(diào)用admin模塊,或者不想讓它調(diào)用有DB寫權(quán)限的模塊奈应,因為這之間存在很脆弱澜掩,很容易導(dǎo)致XSS攻擊。每個模塊不應(yīng)該能做所有的事情杖挣,但是當前大多數(shù)架構(gòu)里的JavaScript代碼都有這種的問題肩榕。提供一個中間層來控制,哪個模塊可以訪問那個授權(quán)的部分惩妇,也就是說点把,該模塊最多只能做到我們所授權(quán)的那部分。

建議的架構(gòu)

我們本文的重點來了屿附,這次我們提議的架構(gòu)使用了我們都很熟知的設(shè)計模式:module, facade和mediator郎逃。

和傳統(tǒng)的模型不一樣的是,為了解耦各個模塊挺份,我們只讓模塊發(fā)布一些event事件褒翰,mediator模式可以負責從這些模塊上訂閱消息message,然后控制通知的response匀泊,facade模式用戶限制各模塊的權(quán)限优训。

以下是我們要注意講解的部分:

1 設(shè)計模式

1.1 模塊論

1.1.1 綜述

1.1.2 Module模式

1.1.3 對象自面量

1.1.4 CommonJS模塊

1.2 Facade模式

1.3 Mediator模式

2 應(yīng)用到你的架構(gòu)

2.1 Facade - 核心抽象

2.2 Mediator - 程序核心

2.3 緊密聯(lián)合運作起來

模塊論

大家可能都或多或少地使用了模塊化的代碼,模塊是一個完整的強健程序架構(gòu)的一部分各聘,每個模塊都是為了單獨的目的為創(chuàng)建的揣非,回到Gmail,我們來個例子躲因,chat聊天模塊看起來是個單獨的一部分早敬,其實它是有很多單獨的子模塊來構(gòu)成忌傻,例如里面的表情模塊其實就是單獨的子模塊,也被用到了發(fā)送郵件的窗口上搞监。

另外一個是模塊可以動態(tài)加載水孩,刪除和替換。

在JavaScript里琐驴,我們又幾種方式來實現(xiàn)模塊俘种,大家熟知的是module模式和對象字面量,如果你已經(jīng)熟悉這些,請忽略此小節(jié)绝淡,直接跳到CommonJS部分宙刘。

Module模式

module模式是一個比較流行的設(shè)計模式,它可以通過大括號封裝私有的變量牢酵,方法悬包,狀態(tài)的,通過包裝這些內(nèi)容茁帽,一般全局的對象不能直接訪問玉罐,在這個設(shè)計模式里屈嗤,只返回一個API潘拨,其它的內(nèi)容全部被封裝成私有的了。

另外饶号,這個模式和自執(zhí)行的函數(shù)表達式比較相似铁追,唯一的不同是module返回的是對象,而自執(zhí)行函數(shù)表達式返回的是function茫船。

眾所周知琅束, JavaScript不想其它語言一樣有訪問修飾符,不能為每個字段或者方法聲明private,public修飾符算谈,那這個模式我們是如何實現(xiàn)的呢涩禀?那就是return一個對象,里面包括一些公開的方法然眼,這些方法有能力去調(diào)用內(nèi)部的對象艾船。

看一下,下面的代碼高每,這段代碼是一個自執(zhí)行代碼屿岂,聲明里包括了一個全局的對象basketModule, basket數(shù)組是一個私有的鲸匿,所以你的整個程序是不能訪問這個私有數(shù)組的爷怀,同時我們return了一個對象,其內(nèi)包含了3個方法(例如addItem,getItemCount,getTotal)带欢,這3個方法可以訪問私有的basket數(shù)組运授。

varbasketModule = (function() {

varbasket = [];//private

return{//exposed to public

addItem:function(values) {

basket.push(values);

},

getItemCount:function() {

returnbasket.length;

},

getTotal:function(){

varq =this.getItemCount(),p=0;

while(q--){

p+= basket[q].price;

}

returnp;

}

}

}());

同時注意烤惊,我們return的對象直接賦值給了basketModule,所以我們可以像下面一樣使用:

//basketModule is an object with properties which can also be methods

basketModule.addItem({item:'bread',price:0.5});

basketModule.addItem({item:'butter',price:0.3});

console.log(basketModule.getItemCount());

console.log(basketModule.getTotal());

//however, the following will not work:

console.log(basketModule.basket);//(undefined as not inside the returned object)

console.log(basket);//(only exists within the scope of the closure)

那在各個流行的類庫(如Dojo, jQuery)里是如何來做呢徒坡?

Dojo

Dojo試圖使用dojo.declare來提供class風格的聲明方式撕氧,我們可以利用它來實現(xiàn)Module模式,例如如果你想再store命名空間下聲明basket對象喇完,那么可以這么做:

//traditional way

varstore = window.store || {};

store.basket = store.basket || {};

//using dojo.setObject

dojo.setObject("store.basket.object", (function() {

varbasket = [];

functionprivateMethod() {

console.log(basket);

}

return{

publicMethod:function(){

privateMethod();

}

};

}()));

結(jié)合dojo.provide一起來使用伦泥,非常強大。

YUI

下面的代碼是YUI原始的實現(xiàn)方式:

YAHOO.store.basket =function() {

//"private" variables:

varmyPrivateVar = "I can be accessed only within YAHOO.store.basket .";

//"private" method:

varmyPrivateMethod =function() {

YAHOO.log("I can be accessed only from within YAHOO.store.basket");

}

return{

myPublicProperty: "I'm a public property.",

myPublicMethod:function() {

YAHOO.log("I'm a public method.");

//Within basket, I can access "private" vars and methods:

YAHOO.log(myPrivateVar);

YAHOO.log(myPrivateMethod());

//The native scope of myPublicMethod is store so we can

//access public members using "this":

YAHOO.log(this.myPublicProperty);

}

};

} ();

jQuery

jQuery里有很多Module模式的實現(xiàn)锦溪,我們來看一個不同的例子不脯,一個library函數(shù)聲明了一個新的library,然后創(chuàng)建該library的時候刻诊,在document.ready里自動執(zhí)行init方法防楷。

functionlibrary(module) {

$(function() {

if(module.init) {

module.init();

}

});

returnmodule;

}

varmyLibrary = library(function() {

return{

init:function() {

/*implementation*/

}

};

}());

對象自面量

對象自面量使用大括號聲明,并且使用的時候不需要使用new關(guān)鍵字则涯,如果對一個模塊里的屬性字段的publice/private不是很在意的話复局,可以使用這種方式,不過請注意這種方式和JSON的不同粟判。對象自面量:var item={name: "tom", value:123}JSON:var item={"name":"tom", "value":123}亿昏。

varmyModule = {

myProperty: 'someValue',

//object literals can contain properties and methods.

//here, another object is defined for configuration

//purposes:

myConfig: {

useCaching:true,

language: 'en'

},

//a very basic method

myMethod:function() {

console.log('I can haz functionality?');

},

//output a value based on current configuration

myMethod2:function() {

console.log('Caching is:' + (this.myConfig.useCaching) ? 'enabled' : 'disabled');

},

//override the current configuration

myMethod3:function(newConfig) {

if(typeofnewConfig == 'object') {

this.myConfig = newConfig;

console.log(this.myConfig.language);

}

}

};

myModule.myMethod();//I can haz functionality

myModule.myMethod2();//outputs enabled

myModule.myMethod3({ language: 'fr', useCaching:false});//fr

CommonJS

關(guān)于 CommonJS的介紹,這里就不多說了档礁,博客園有很多帖子都有介紹角钩,我們這里要提一下的是CommonJS標準里里有2個重要的參數(shù)exports和require,exports是代表要加載的模塊呻澜,require是代表這些加載的模塊需要依賴其它的模塊递礼,也需要將它加載進來。

/*

Example of achieving compatibility with AMD and standard CommonJS by putting boilerplate around the standard CommonJS module format:

*/

(function(define){

define(function(require,exports){

//module contents

vardep1 = require("dep1");

exports.someExportedFunction =function(){...};

//...

});

})(typeofdefine=="function"?define:function(factory){factory(require,exports)});

有很多CommonJS標準的模塊加載實現(xiàn)羹幸,我比較喜歡的是RequireJS脊髓,它能否非常好的加載模塊以及相關(guān)的依賴模塊,來一個簡單的例子栅受,例如需要將圖片轉(zhuǎn)化成ASCII碼将硝,我們先加載encoder模塊,然后獲取他的encodeToASCII方法窘疮,理論上代碼應(yīng)該是如下:

varencodeToASCII = require("encoder").encodeToASCII;

exports.encodeSomeSource =function(){

//其它操作以后袋哼,然后調(diào)用encodeToASCII

}

但是上述代碼并沒用工作,因為encodeToASCII函數(shù)并沒用附加到window對象上闸衫,所以不能使用涛贯,改進以后的代碼需要這樣才行:

define(function(require, exports, module) {

varencodeToASCII = require("encoder").encodeToASCII;

exports.encodeSomeSource =function(){

//process then call encodeToASCII

}

});

CommonJS 潛力很大,但是由于大叔不太熟蔚出,所以就不過多地介紹了弟翘。

Facade模式

Facade模式在本文架構(gòu)里占有重要角色虫腋,關(guān)于這個模式很多JavaScript類庫或者框架里都有體現(xiàn),其中最大的作用稀余,就是包括High level的API悦冀,以此來隱藏具體的實現(xiàn),這就是說睛琳,我們只暴露接口盒蟆,內(nèi)部的實現(xiàn)我們可以自己做主,也意味著內(nèi)部實現(xiàn)的代碼可以很容易的修改和更新师骗,比如今天你是用jQuery來實現(xiàn)的历等,明天又想換YUI了,這就非常方便了辟癌。

下面這個例子了寒屯,可以看到我們提供了很多私有的方法,然后通過暴露一個簡單的 API來讓外界執(zhí)行調(diào)用內(nèi)部的方法:

varmodule = (function() {

var_private = {

i: 5,

get:function() {

console.log('current value:' +this.i);

},

set:function(val) {

this.i = val;

},

run:function() {

console.log('running');

},

jump:function() {

console.log('jumping');

}

};

return{

facade:function(args) {

_private.set(args.val);

_private.get();

if(args.run) {

_private.run();

}

}

}

} ());

module.facade({run:true,val:10});

//outputs current value: 10, running

Facade和下面我們所說的mediator的區(qū)別是黍少,facade只提供現(xiàn)有存在的功能寡夹,而mediator可以增加新功能。

Mediator模式

講modiator之前厂置,我們先來舉個例子菩掏,機場飛行控制系統(tǒng),也就是傳說中的塔臺农渊,具有絕對的權(quán)利患蹂,他可以控制任何一架飛機的起飛和降落時間以及地方或颊,而飛機和飛機之前不允許通信砸紊,也就是說塔臺是機場的核心,mediator就相當于這個塔臺囱挑。

mediator就是用在程序里有多個模塊醉顽,而你又不想讓各個模塊有依賴的話,那通過mediator模式可以達到集中控制的目的平挑。實際場景中也是游添,mediator封裝了很多不想干的模塊,讓他們通過mediator聯(lián)系在一起通熄,同時也松耦合他們唆涝,使得他們之間必須通過mediator才能通信。

那mediator模式的優(yōu)點是什么唇辨?那就是解耦廊酣,如果你之前對觀察者模式比較了解的話,那理解下面的mediator圖就相對簡單多了赏枚,下圖是一個high level的mediator模式圖:

想想一下亡驰,各模塊是發(fā)布者晓猛,mediator既是發(fā)布者又是訂閱者。

Module 1向Mediator廣播一個實際凡辱,說需要做某事

Mediator捕獲消息以后戒职,立即啟動處理該消息需要使用的Module 2,Module 2處理結(jié)束以后返回信息給Mediator

與此同時透乾,Mediator也啟動了Module 3洪燥,當接受Module 2 返回消息的時候自動記錄日志到Module 3里

可以看到,各模塊之間并沒有通信乳乌,另外Mediator也可以實現(xiàn)監(jiān)控各模塊狀態(tài)的功能蚓曼,例如如果Module 3出錯了,Mediator可以暫時只想其它模塊钦扭,然后重啟Module 3纫版,然后繼續(xù)執(zhí)行。

回顧一下客情,可以看到其弊,Mediator的優(yōu)點是:松耦合的模塊由同一的Mediator來控制,模塊只需要廣播和監(jiān)聽事件就可以了膀斋,而模塊之間不需要直接聯(lián)系梭伐,另外,一次信息的處理可以使用多個模塊仰担,也方便我們以后統(tǒng)一的添加新的模塊到現(xiàn)有的控制邏輯里糊识。

確定是:由于所有的模塊直接都不能直接通信,所有相對來說摔蓝,性能方面可能會有少許下降赂苗,但是我認為這是值得的。

我們根據(jù)上面的講解來一個簡單的Demo:

varmediator = (function(){

varsubscribe =function(channel, fn){

if(!mediator.channels[channel]) mediator.channels[channel] = [];

mediator.channels[channel].push({ context:this, callback: fn });

returnthis;

},

publish =function(channel){

if(!mediator.channels[channel])returnfalse;

varargs = Array.prototype.slice.call(arguments, 1);

for(vari = 0, l = mediator.channels[channel].length; i < l; i++) {

varsubscription = mediator.channels[channel][i];

subscription.callback.apply(subscription.context, args);

}

returnthis;

};

return{

channels: {},

publish: publish,

subscribe: subscribe,

installTo:function(obj){

obj.subscribe = subscribe;

obj.publish = publish;

}

};

}());

然后有2個模塊分別調(diào)用:

//Pub/sub on a centralized mediator

mediator.name = "tim";

mediator.subscribe('nameChange',function(arg){

console.log(this.name);

this.name = arg;

console.log(this.name);

});

mediator.publish('nameChange', 'david');//tim, david

//Pub/sub via third party mediator

varobj = { name: 'sam' };

mediator.installTo(obj);

obj.subscribe('nameChange',function(arg){

console.log(this.name);

this.name = arg;

console.log(this.name);

});

obj.publish('nameChange', 'john');//sam, john

應(yīng)用Facade: 應(yīng)用程序核心的抽象

一個facade是作為應(yīng)用程序核心的一個抽象來工作的贮尉,在mediator和模塊之間負責通信拌滋,各個模塊只能通過這個facade來和程序核心進行通信。作為抽象的職責是確保任何時候都能為這些模塊提供一個始終如一的接口(consistent interface)猜谚,和sendbox controller的角色比較類似败砂。所有的模塊組件通過它和mediator通信,所以facade需要是可靠的魏铅,可信賴的昌犹,同時作為為模塊提供接口的功能,facade還需要扮演另外一個角色览芳,那就是安全控制斜姥,也就是決定程序的哪個部分可以被一個模塊訪問,模塊組件只能調(diào)用他們自己的方法,并且不能訪問任何未授權(quán)的內(nèi)容疾渴。例如千贯,一個模塊可能廣播dataValidationCompletedWriteToDB,這里的安全檢查需要確保該模塊擁有數(shù)據(jù)庫的寫權(quán)限搞坝。

總之搔谴,mediator只有在facade授權(quán)檢測以后才能進行信息處理。

應(yīng)用Mediator:應(yīng)用程序的核心

Mediator是作為應(yīng)用程序核心的角色來工作的桩撮,我們簡單地來說一下他的職責敦第。最核心的工作就是管理模塊的生命周期(lifecycle),當這個核心撲捉到任何信息進來的時候店量,他需要判斷程序如何來處理——也就是說決定啟動或停止哪一個或者一些模塊芜果。當一個模塊開始啟動的時候,它應(yīng)該能否自動執(zhí)行融师,而不需要應(yīng)用程序核心來決定是否該執(zhí)行(比如右钾,是否要在DOM ready的時候才能執(zhí)行),所以說需要模塊自身需要去判定旱爆。

你可能還有問題舀射,就是一個模塊在什么情況下才會停止。當程序探測到一個模塊失敗了怀伦,或者是出錯了脆烟,程序需要做決定來防止繼續(xù)執(zhí)行該模塊里的方法,以便這個組件可以重新啟動房待,目的主要是提高用戶體驗邢羔。

另外,該核心應(yīng)該可以動態(tài)添加或者刪除模塊桑孩,而不影響其他任何功能拜鹤。常見的例子是,一個模塊在頁面加載初期是不可用洼怔,但是用戶操作以后署惯,需要動態(tài)加載這個模塊然后執(zhí)行左驾,就像Gmail里的chat聊天功能一樣镣隶,從性能優(yōu)化的目的來看,應(yīng)該是很好理解的吧诡右。

異常錯誤處理安岂,也是由應(yīng)用程序核心來處理的,另外各模塊在廣播信息的時候帆吻,也廣播任何錯誤到該核心里域那,以便程序核心可以根據(jù)情況去停止/重啟這些模塊。這也是松耦合架構(gòu)一個很重要的部分,我們不需要手工改變?nèi)魏文K次员,通過mediator使用發(fā)布/訂閱就可以來做到這個败许。

組裝起來

各模塊包含了程序里各種各樣的功能,他們有信息需要處理的時候淑蔚,發(fā)布信息通知程序(這是他們的主要職責)市殷,下面的QA小節(jié)里提到了,模塊可以依賴一些DOM工具操作方法刹衫,但是不應(yīng)該和系統(tǒng)的其它模塊有依賴醋寝,一個模塊不應(yīng)該關(guān)注如下內(nèi)容:

哪個對象或者模塊訂閱了這個模塊發(fā)布的信息

這些對象是客戶端對象還是服務(wù)器端對象

多少對象訂閱了你的信息

Facade抽象應(yīng)用程序的核心,避免各個模塊之間直接通信带迟,它從各模塊上訂閱信息音羞,也負責授權(quán)檢測,確保每個模塊有用自己單獨的授權(quán)仓犬。

Mediator(應(yīng)用程序核心)使用mediator模式扮演發(fā)布/訂閱管理器的角色嗅绰,負責模塊管理以及啟動/停止模塊執(zhí)行,可以動態(tài)加載以及重啟有錯誤的模塊搀继。

這個架構(gòu)的結(jié)果是:各模塊之間沒有依賴办陷,因為松耦合的應(yīng)用,它們可以很容易地被測試和維護律歼,各模塊可以很容易地在其它項目里被重用民镜,也可以在不影響程序的情況下動態(tài)添加和刪除。

發(fā)布Pub/訂閱Sub的延伸:自動注冊事件(Automatic Event Registration)

關(guān)于自動注冊事件险毁,需要遵守一定的命名規(guī)范制圈,比如如果一個模塊發(fā)布了一個名字為messageUpdate的事件,那么所有帶有messageUpdate方法的模塊都會被自動執(zhí)行畔况。有好處也有利弊鲸鹦,具體實現(xiàn)方式,可以看我另外一篇帖子:jQuery自定義綁定的魔法升級版跷跪。

QA

1.有可能不使用facade或者類似的sandbox模式么馋嗜?

盡管架構(gòu)的大綱里提出了facade可以實現(xiàn)授權(quán)檢查的功能,其實完全可能由mediator去做吵瞻,輕型架構(gòu)要做的事情其實是幾乎一樣的葛菇,那就是解耦,確保各模塊直接和應(yīng)用程序核心通信是沒問題的就行橡羞。

2.你提高了模塊直接不能有依賴眯停,是否意味著不能依賴任何第三方類庫(例如jQuery)。

這其實就是一個兩面性的問題卿泽,我們上面說到了莺债,一個模塊也許有一些子模塊,或者基礎(chǔ)模塊,比如基本的DOM操作工具類等齐邦,在這個層面上講椎侠,我們是可以用第三方類庫的,但是請確保措拇,我們可以很容易地能否替換掉他們肺蔚。

3.我喜歡這個架構(gòu),并且想開始使用這個架構(gòu)儡羔,有任何代碼樣本可以參考么宣羊?

我打算去搞一份代碼樣本供大家參考,不過在這之前汰蜘,你可以參考Andrew Burgees的帖子Writing Modular JavaScript仇冯。

4.如果模塊需要和應(yīng)用程序核心直接通信,是否可行族操?

技術(shù)上來將苛坚,沒有理由現(xiàn)在模塊不能和應(yīng)用程序核心直接通信,但是對于大多數(shù)應(yīng)用體驗來說色难,還是不要泼舱。既然你選擇了這個架構(gòu),那就要遵守該架構(gòu)所定義的規(guī)則枷莉。

致謝

感謝Nicholas Zakas的原始貼娇昙,將思想總結(jié)在一起,感謝Andree Hansson的technical review,感謝Rebecca Murphey, Justin Meyer, John Hann, Peter Michaux, Paul Irish和Alex Sexton,他們所有的人都提供了和本Session相關(guān)的很多資料。

也非常感謝博客園的湯姆大叔(TomXu)笤妙,將本Session的內(nèi)容整理成中文版本冒掌,如對你有用,請推薦一把蹲盘。


轉(zhuǎn)自:http://www.cnblogs.com/TomXu/archive/2011/12/14/2286225.html ? 謝謝湯姆大叔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末股毫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子召衔,更是在濱河造成了極大的恐慌铃诬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苍凛,死亡現(xiàn)場離奇詭異趣席,居然都是意外死亡,警方通過查閱死者的電腦和手機毫深,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門吩坝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哑蔫,你說我怎么就攤上這事。” “怎么了闸迷?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵嵌纲,是天一觀的道長。 經(jīng)常有香客問我腥沽,道長逮走,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任今阳,我火速辦了婚禮师溅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盾舌。我一直安慰自己墓臭,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布妖谴。 她就那樣靜靜地躺著窿锉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膝舅。 梳的紋絲不亂的頭發(fā)上嗡载,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音仍稀,去河邊找鬼洼滚。 笑死,一個胖子當著我的面吹牛技潘,可吹牛的內(nèi)容都是我干的判沟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼崭篡,長吁一口氣:“原來是場噩夢啊……” “哼挪哄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琉闪,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤迹炼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颠毙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斯入,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年蛀蜜,在試婚紗的時候發(fā)現(xiàn)自己被綠了刻两。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡滴某,死狀恐怖磅摹,靈堂內(nèi)的尸體忽然破棺而出滋迈,到底是詐尸還是另有隱情,我是刑警寧澤户誓,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布饼灿,位于F島的核電站,受9級特大地震影響帝美,放射性物質(zhì)發(fā)生泄漏碍彭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一悼潭、第九天 我趴在偏房一處隱蔽的房頂上張望庇忌。 院中可真熱鬧,春花似錦舰褪、人聲如沸皆疹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墙基。三九已至,卻和暖如春刷喜,著一層夾襖步出監(jiān)牢的瞬間残制,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工掖疮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留初茶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓浊闪,卻偏偏與公主長得像恼布,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子搁宾,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品折汞,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式盖腿。簡單...
    舟漁行舟閱讀 7,777評論 2 17
  • 單例模式 適用場景:可能會在場景中使用到對象爽待,但只有一個實例,加載時并不主動創(chuàng)建翩腐,需要時才創(chuàng)建 最常見的單例模式鸟款,...
    Obeing閱讀 2,076評論 1 10
  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,088評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)茂卦,斷路器芬萍,智...
    卡卡羅2017閱讀 134,699評論 18 139
  • 9月的最后一天去大學城聽了Another Eason's life演唱會倡鲸。 01 陽光多燦爛 可我的自由行駛地緩慢...
    加載的丸閱讀 554評論 0 1