這篇文章主要介紹了JavaScript的Backbone.js框架入門學(xué)習(xí)指引, 其中特別講到了Backbone中的關(guān)鍵部分Router路由器,需要的朋友可以參考下
1.簡介
最近在做一個(gè)大型網(wǎng)上銀行項(xiàng)目前端的優(yōu)化,需要使用一個(gè)胖客戶端的優(yōu)化,大概思路就是前端通過Ajax 請求去后端獲取數(shù)據(jù),以Jason的格式返回,然后顯示在頁面上。由于這個(gè)系統(tǒng)非常龐大,胖客戶端方案難免需要在客戶端寫大量的JS代碼。我想對于任何團(tuán)隊(duì)來說,大量的,非結(jié)構(gòu)化的代碼維護(hù)起來都非常的不方便。所以BackBone進(jìn)入了我的視線。
它提供了一種途徑可以讓你結(jié)構(gòu)化你的JS代碼,讓你以面向?qū)ο蟮姆绞絹斫M織你的前端JS代碼。這就好比我們在前端應(yīng)用Domain Driven Design. 我們可以把一個(gè)非常大的項(xiàng)目按模塊切分。 每個(gè)模塊里面又可以按照BackBone的要求切分成View, Model, Router。
通過backbone,你可以把你的數(shù)據(jù)當(dāng)作Models,通過Models你可以創(chuàng)建數(shù)據(jù),進(jìn)行數(shù)據(jù)驗(yàn)證,銷毀或者保存到服務(wù)器上。當(dāng)界面上的操作引起model中屬性的變化時(shí),model會觸發(fā)change的事件;那些用來顯示model狀態(tài)的views會接受到model觸發(fā)change的消息,進(jìn)而發(fā)出對應(yīng)的響應(yīng),并且重新渲染新的數(shù)據(jù)到界面。在一個(gè)完整的backbone應(yīng)用中,你不需要寫那些膠水代碼來從DOM中通過特殊的id來獲取節(jié)點(diǎn),或者手工的更新HTML頁面,因?yàn)樵趍odel發(fā)生變化時(shí),views會很簡單的進(jìn)行自我更新。
2.特點(diǎn)
Backbone是一個(gè)輕量級的前端框架,用于結(jié)構(gòu)化管理頁面中的大量JS,建立與服務(wù)器、視圖間的無縫連接,為構(gòu)建復(fù)雜的應(yīng)用提供基礎(chǔ)框架。
下面我先簡單地闡述下Backbone的主要特點(diǎn)及特性:
2.1 輕量級
Backbone的源碼只有1000行左右(去注釋和空行后),文件大小只有16KB,加上依賴庫Underscore,也僅有29KB。
你只需要花一點(diǎn)時(shí)間,就能輕松了解Backbone內(nèi)部實(shí)現(xiàn);或編寫少量代碼,來重載Backbone的部分機(jī)制;如果你想在Backbone的基礎(chǔ)上做二次開發(fā),也并不是一件復(fù)雜的事情。
2.2 結(jié)構(gòu)化
Backbone可以輕松將頁面中的數(shù)據(jù)、邏輯、視圖解耦,依照Backbone進(jìn)行代碼結(jié)構(gòu)組織,你可以將項(xiàng)目中的數(shù)據(jù)交互、業(yè)務(wù)邏輯、用戶界面等工作,分配給多個(gè)同事同時(shí)開發(fā),并能有序地組織到一起。同時(shí),這對于大型和復(fù)雜項(xiàng)目的維護(hù)開發(fā)非常有幫助。
2.3 繼承機(jī)制
在Backbone中,模塊是可以被繼承的,你可以通過面向?qū)ο蟮姆绞綄?yīng)用中的數(shù)據(jù)模型、集合、視圖有序地組織,讓整個(gè)架構(gòu)更加清晰;也可以方便地重載和擴(kuò)展自定義方法。
2.4 建立與服務(wù)器的無縫連接
在Backbone中內(nèi)置了一套與服務(wù)器數(shù)據(jù)的交互規(guī)則(如果你了解REST架構(gòu),就能夠輕松地理解它們),而數(shù)據(jù)的同步工作會在Model中自動(dòng)進(jìn)行,前端開發(fā)人員只需對客戶端數(shù)據(jù)的進(jìn)行操作,Backbone會自動(dòng)將操作的數(shù)據(jù)同步到服務(wù)器。
這是件非常有趣的事情,因?yàn)榉?wù)器數(shù)據(jù)接口對前端開發(fā)者來說是透明的,他們不需要再關(guān)心如何和服務(wù)器交互。
然而服務(wù)器提供的數(shù)據(jù)接口也需要兼容Backbone的規(guī)則,對于一個(gè)新的項(xiàng)目來說,我們可以嘗試使用這套規(guī)則來構(gòu)建接口。但如果你的項(xiàng)目中已經(jīng)有一套穩(wěn)定的接口,你可能會擔(dān)心接口改造的風(fēng)險(xiǎn)。
沒關(guān)系,我們可以通過重載Backbone.sync方法來適配現(xiàn)有的數(shù)據(jù)接口,針對不同的客戶端環(huán)境,我們還可以實(shí)現(xiàn)不同的數(shù)據(jù)交互方式。例如:用戶通過PC瀏覽器使用服務(wù)時(shí),數(shù)據(jù)會實(shí)時(shí)同步到服務(wù)器;而用戶通過移動(dòng)終端使用服務(wù)時(shí),考慮到網(wǎng)絡(luò)環(huán)境問題,我們可以先將數(shù)據(jù)同步到本地?cái)?shù)據(jù)庫,在合適的時(shí)候再同步到服務(wù)器。而這些只需要你重載一個(gè)方法就可以實(shí)現(xiàn)。
2.5 界面事件管理
在MVC中,我們希望能將界面展現(xiàn)和業(yè)務(wù)邏輯完全分離,但對于用戶產(chǎn)生的交互事件(如單擊事件),我們卻常常通過類似jQuery中的bind方法進(jìn)行獲取和綁定。
Backbone中的視圖幫助我們將用戶事件和執(zhí)行方法有序的組織起來,這只需要我們聲明一個(gè)簡單的表達(dá)式,例如:
events: {
// 單擊id為”save”的元素時(shí),執(zhí)行視圖的add方法
'click #save': 'add',
'mousedown .button': 'show',
'mouseover .button': 'hide'
}
在表達(dá)式中,事件名稱可以是任意的DOM事件(如click、mouseover、keypress等),元素可以是jQuery支持的任意選擇器(如標(biāo)簽選擇器、id選擇器、class選擇器等)。
視圖會自動(dòng)將表達(dá)式中的事件綁定到選擇器元素,當(dāng)元素的事件被觸發(fā)后,視圖會自動(dòng)調(diào)用表達(dá)式中綁定的方法。
2.6 輕量級模板解析
模板解析是Underscore中提供的一個(gè)方法。為什么我要在介紹Backbone特性時(shí)引入U(xiǎn)nderscore中的方法?因?yàn)樵摲椒軒椭覀兏玫胤蛛x視圖結(jié)構(gòu)和邏輯,且Underscore是Backbone必須依賴的庫。
模板解析方法能允許我們在HTML結(jié)構(gòu)中混合嵌入JS代碼,就像在JSP頁面中嵌入Java代碼一樣:
<ul>
<% for(var i = 0; i < len; i++) { %>
<li><%=data[i].title%></li>
<% } %>
</li>
通過模板解析,我們不需要在動(dòng)態(tài)生成HTML結(jié)構(gòu)時(shí)拼接字符串,更重要的是,我們可以將視圖中的HTML結(jié)構(gòu)獨(dú)立管理(例如:不同的狀態(tài)可能會顯示不同的HTML結(jié)構(gòu),我們可以定義多個(gè)單獨(dú)的模板文件,按需加載和渲染即可)。
2.7 自定義事件管理
在Backbone中,你可以使用on或off方法綁定和移除自定義事件。在任何地方,你都可以使用trigger方法觸發(fā)這些綁定的事件,所有綁定過該事件的方法都會被執(zhí)行,如:
var model = new Backbone.Model();
// 在model對象中向自定義事件custom綁定兩個(gè)函數(shù)
model.on('custom', function(p1, p2) {
// todo
});
model.on('custom', function(p1, p2) {
// todo
});
// 觸發(fā)custom事件,將調(diào)用上面綁定的兩個(gè)函數(shù)
model.trigger('custom', 'value1', 'value2');
// 移除custom事件中綁定的所有方法
model.off('custom');
// 觸發(fā)custom事件,但不會執(zhí)行任何函數(shù),已經(jīng)事件中的函數(shù)已經(jīng)在上一步被移除
model.trigger('custom');
如果你熟悉jQuery,你會發(fā)現(xiàn)它們與jQuery中的bind、unbind和trigger方法非常類似。
另外,Backbone支持一個(gè)特殊事件”all”,當(dāng)在一個(gè)對象中綁定了名為”all”的事件后,該對象在觸發(fā)任何事件時(shí),都會同時(shí)觸發(fā)”all”事件中綁定的方法。有時(shí)這種方法會非常有用,例如我們可以通過”all”事件監(jiān)聽對象狀態(tài)的變化。
3.路由器
在單頁應(yīng)用中,我們通過JavaScript來控制界面的切換和展現(xiàn),并通過AJAX從服務(wù)器獲取數(shù)據(jù)。
可能產(chǎn)生的問題是,當(dāng)用戶希望返回到上一步操作時(shí),他可能會習(xí)慣性地使用瀏覽器“返回”和“前進(jìn)”按鈕,而結(jié)果卻是整個(gè)頁面都被切換了,因?yàn)橛脩舨⒉恢浪幱谕粋€(gè)頁面中。
對于這個(gè)問題,我們常常通過Hash(錨點(diǎn))的方式來記錄用戶的當(dāng)前位置,并通過onhashchange事件來監(jiān)聽用戶的“前進(jìn)”和“返回”動(dòng)作,但我們發(fā)現(xiàn)一些低版本的瀏覽器(例如IE6)并不支持onhashchange事件。
Backbone提供了路由控制功能,通過Backbone提供的路由器,我們能通過一個(gè)簡單的表達(dá)式將路由地址和事件函數(shù)綁定在一起,例如:
var CustomRouter = Backbone.Router.extend({
routes : {
'' : 'index', // 當(dāng)URL Hash在根目錄時(shí)執(zhí)行index方法:url#
'list' : 'getList', // 當(dāng)URL Hash在list節(jié)點(diǎn)時(shí)執(zhí)行g(shù)etList方法:url#list
'detail/:id' : 'query', // 當(dāng)URL Hash在detail節(jié)點(diǎn)時(shí)執(zhí)行query方法,并將detail后的數(shù)據(jù)作為參數(shù)傳遞給query方法:url#list/1001
'*error' : 'showError' // 當(dāng)URL Hash不匹配以上規(guī)則時(shí), 執(zhí)行error方法
},
index : function() {
alert('index');
},
getList : function() {
alert('getList');
},
query : function(id) {
alert('query id: ' + id);
},
showError : function(error) {
alert('error hash: ' + error);
},
});
var custom = new CustomRouter();
Backbone.history.start();
請嘗試將這段代碼復(fù)制到你的頁面中,并依次訪問以下地址(其中URL表示你的頁面地址):
URL
URL#list
URL#detail/1001
URL#hash1
URL#hash2
請?jiān)僭囍褂脼g覽器的“返回”和“前進(jìn)”按鈕來回切換剛剛輸入的地址。
你可以看到,當(dāng)URL Hash發(fā)生變化時(shí),會執(zhí)行所綁定的方法,當(dāng)遇到?jīng)]有定義的Hash時(shí),都會執(zhí)行showError方法,并將未定義的Hash傳遞給該方法。
Backbone默認(rèn)會通過Hash的方式來記錄地址的變化,對于不支持onhashchange的低版本瀏覽器,會通過setInterval心跳監(jiān)聽Hash的變化,因此你不必?fù)?dān)心瀏覽器的兼容性問題。
對于支持HTML5 pushState特性的瀏覽器,Backbone還允許你通過pushState來創(chuàng)建個(gè)性化的URL,但是這需要你的Web服務(wù)器做一些適配。
3. Backbone的適用性
Backbone并不像jQuery那樣具有非常強(qiáng)的適用性,如果你正準(zhǔn)備構(gòu)建一個(gè)大型或復(fù)雜的單頁Web應(yīng)用,那么Backbone再適合不過。
如果想將Backbone應(yīng)用到你的網(wǎng)站頁面中,且頁面中并沒有復(fù)雜的邏輯和結(jié)構(gòu),那么這只會讓你的頁面更加繁瑣和難以維護(hù)。
如果你的項(xiàng)目并不復(fù)雜,但你卻深深喜歡它的某個(gè)特性(可能是數(shù)據(jù)模型、視圖管理或路由器),那么你可以將這部分源碼從Backbone中抽取出來,因?yàn)樵贐ackbone中,各模塊間的依賴并不是很強(qiáng),你能輕易的獲取并使用其中的某一個(gè)模塊。
4. 依賴庫
你不能獨(dú)立使用Backbone,因?yàn)樗幕A(chǔ)函數(shù)、DOM操作、AJAX都依賴于第三方庫。
4.1 Underscore
(必選)
Underscore是一個(gè)用于提高開發(fā)效率的基礎(chǔ)函數(shù)庫,它封裝了對集合、數(shù)組、對象、函數(shù)的常用操作,就像jQuery封裝DOM對象一樣,你能通過Underscore輕易地訪問和操作JavaScript內(nèi)部對象。
Underscore還提供了一些非常實(shí)用的函數(shù)方法,如:函數(shù)節(jié)流、模板解析等。
關(guān)于Underscore中一些主要的方法,我會在下一章詳細(xì)介紹,但在此之前你必須了解:Underscore是Backbone必須依賴的庫,因?yàn)樵贐ackbone中許多實(shí)現(xiàn)都是基于Underscore。
4.2 jQuery和Zepto
(可選)
相信你對jQuery一定不會陌生,它是一個(gè)跨瀏覽器的DOM和AJAX框架。
而對于Zepto你可以理解為“移動(dòng)版的jQuery”,因?yàn)樗?、更快、更適合在移動(dòng)終端設(shè)備的瀏覽器上運(yùn)行,它與jQuery語法相同,因此你能像使用jQuery那樣使用它。
Zepto目前僅支持Webkit內(nèi)核的瀏覽器,因此它能兼容iOS、Adnroid、塞班、黑莓和Meego等大部分移動(dòng)系統(tǒng),而對于Windows Phone或Firefox OS,它暫時(shí)還不支持。
因?yàn)閖Query和Zepto語法相同,因此對于Backbone來說,你無論是使用jQuery還是Zepto,都沒有問題(當(dāng)然,你不可能兩個(gè)同時(shí)都用到)。
在Backbone中,DOM選擇器、DOM事件和AJAX,都使用了jQuery的方法。這里之所以所它們是可選的,是假設(shè)你沒有用到Backbone中的視圖和AJAX數(shù)據(jù)同步功能,那么就不需要導(dǎo)入它們。
如果你不想使用jQuery或Zepto,而是使用其它的、或自定義庫,只要你的庫中實(shí)現(xiàn)了與jQuery語法相同的DOM選擇器、事件管理和AJAX方法,那么就不會任何問題。
Backbone允許你通過setDomLibrary方法動(dòng)態(tài)配置需要使用的第三方庫,這種情況常常用于:
你的自定義庫雖然包含了和jQuery相同語法的方法,但全局變量并不是$,而且你想保持現(xiàn)有的命名。這時(shí)你可以通過setDomLibrary方法將其設(shè)置為Backbone內(nèi)部引用的對象。
你希望通過檢查用戶的環(huán)境,來決定更適合使用哪一個(gè)庫。例如:如果用戶使用PC瀏覽器訪問,則載入jQuery,如果用戶通過移動(dòng)終端訪問,則載入Zepto。