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