什么是 Backbone.js


Backbone.js 是一個在JavaScript環境下的 模型-視圖-控制器 (MVC) 框架。任何接觸較大規模項目的開發人員一定會苦惱於各種瑣碎的事件回調邏輯、以及金字塔般的代碼。而且,在傳統的Web應用程序代碼中,不可避免的都有在應用邏輯中加入顯示數據的代碼的情況。當項目規模愈發變大時,這種形式的代碼變得越發的難以維護,因為任何在主干邏輯中的變更都可能影響到數據顯示邏輯,反之亦然。

Backbone就是要來解決這樣的代碼耦合的問題。它通過提供一個控制層-顯示層的框架,以及模版(template)來分離各自邏輯。這樣的MVC框架類似於傳統意義上桌面程序以及服務器端程序的程序框架。

 

模型 Model

運用Backbone框架的程序的核心是“模型”。多數情況下,“模型”代表了數據庫中存儲的一種對象結構。

Backbone的設計思想是,讓模型服務於存儲、獲取和更改數據。注意,有另外一些MVC框架的設計是讓控制器負責更改數據。可是backbone的設計思想是,控制器的作用僅僅是用來處理從視圖層來的用戶請求、以及訪問相對應的模型層。而模型層自身則需要負責(從數據源中)獲取數據、以及數據封裝。

下面的例子闡釋了在Backbone里,數據模型是怎樣聲明和初始化的。

Stooge = Backbone.Model.extend({
    defaults: {
        'name': 'Guy Incognito',
        'power': 'Classified',
        'friends': [],
    },
    initialize: function () {
        // Do initialization
    }
});
var account = new Stooge({
    name: 'Larry',
    power: 'Baldness',
    friends: ['Curly', 'Moe']
});

在backbone中,要創建“模型”,可以擴展 Backbone.Model 並且提供實例的屬性。在上述例子中, extend 函數給 Stooge 類建立了一個原型鏈,因此只需要通過Stooge你就可以訪問 模型 中的所有屬性。並且,由於 extend 正確的設置了原型鏈,因此通過 extend 創建的子類 (subclasses) 也可以被深度擴展。Extend在backbone中是很重要的概念,在多數JavaScript庫里面,extend函數用來處理從一個對象復制到另一個對象。在backbone里面,extend函數同樣也會創建一個構造函數,因此你可以用來初始化一個類,再把它復制到新的類,從而達到多層擴展。

 

視圖 View

視圖表示了在一種“模型”的基礎上展示數據的方法。根據應用邏輯上下文的不同,視圖決定了怎樣展示數據。比如,在加拿大,一個公民可以選擇使用短格式的出生證明或者是較長格式的出生證明。兩種出生證明文檔都包括了一樣的關鍵信息(Model),但是細節程度不同(View)。在Backbone里,視圖提供了一個“窗口”來查看一個模型中的數據、協助監聽在界面上的用戶交互或者數據模型上的變化,從而觸發數據顯示的更新。

下面的是在網頁中顯示出生證明數據的例子。

<div id="certificate"></div>

<script type="text/javascript"> CertificateView = Backbone.View.extend({ initialize: function () { this.render(); }, render: function () { $(this.el).html("<h1>Guy Incognito</h1><p>DOB: March 2, 1967</p>"); } }); var certificate_view = new CertificateView({ el: $("#certificate") }); </script>

在上述代碼中,聲明了一個 CertificateView 類,作為出生證明的視圖實例。它同樣使用了backbone的extend方法來擴展了一個定制的視圖結構,也就是出生證明這個結構(使用extend方法就跟上一節中的擴展“模型”一樣)。這個定制的視圖包含了兩個方法:

initialize 方法 —— 當一個該視圖結構的實例被創建時,這個方法被執行。在上述代碼示例中,initialize要做的就是立即觸發render函數來處理數據顯示。

render方法 —— 調整DOM節點里面的內容,達到顯示數據的目的。

在backbone里,所有的“視圖”都不可避免的要跟DOM樹操作打交道。在上述代碼里,我們實例化視圖時,傳入了一個需要顯示數據的DOM節點。如果你沒有傳入任何DOM節點,那么backbone將會處理頁面上所有的div節點。

 

視圖模板 View Template

上一節的代碼有一個潛在的問題:在JavaScript代碼里出現了生成HTML代碼的邏輯。因此,我們需要使用“視圖模板”來分離耦合的處理邏輯。注意,Backbone里的 View 並不包含template本身,它只提供view的控制邏輯,而具體生成template的功能是靠其依賴庫 underscore 的 .template() 方法實現的。

具體的說,我們把生成HTML的邏輯從視圖層的render函數中移了出來,把它放在一個<script>標簽下。為了防止瀏覽器把它當作真正的JavaScript代碼去處理它,<script>的type屬性被設置成 text/template (而不是默認的 text/javascript)。如果不重新聲明默認的type屬性,那么瀏覽器就會用JS的引擎去處理那里面的內容,而由於視圖模板常常是HTML代碼段,所以通常會拋出JS語句解析錯誤。

<div id="certificate"></div>

<script type="text/template" id="tpl-certificate"> < h1 > <%= name %> < /h1> <p> DOB: <%= dob %> </p > </script>
<script type="text/javascript"> CertificateView = Backbone.View.extend({ template: _.template($('#tpl-certificate').html()), initialize: function () { this.render(); }, render: function () { var templateArgs = { name: "Guy Incognito", dob: "March 2, 1967" }; $(this.el).html(this.template(templateArgs)); } }); var certificate_view = new CertificateView({ el: $("#certificate") }); </script>

在上述代碼中,name和date被當作視圖模板的參數變量。在模板里使用變量時,需要加上<% 和 %>標簽。如果需要輸出變量,則需要使用 <%= 標簽,跟JSP的語法一樣。

在視圖中使用模板需要注意兩步:第一步,視圖邏輯必須知道采用哪一個模板來渲染數據;第二步,使用模板時需要傳入變量參數。

我們使用了jQuery的 .html() 函數來獲取<script>標簽下的模板內容。 接着,我們又使用了 underscore 的 .template() 函數 根據參數變量和模板原文 來組合生成最終要顯示的HTML代碼。

現在,使用 實例名.render() 就可以實現數據顯示的工作了。 在render函數里,首先聲明了需要傳入模板的參數變量(大多數情況下,這個需要從模型層獲取),接着使用template函數生成HTML代碼,最后使用 jQuery.html()更改DOM節點中的內容。

 

集合 Collection

在Backbone里,集合是模型Model的有序組合,我們可以在集合上綁定 "change" 事件,從而當集合中的模型發生變化時獲得通知,集合也可以監聽 "add" 和 “remove" 事件, 從服務器更新。同時,你也可以使用 Underscore 提供的方法(例如 forEach, filter, sortBy等等)。

你可以通過擴展 Backbone.Collection 創建一個 集合。

var Library = Backbone.Collection.extend({
  model: Book
});

盡管使用集合有開銷,為什么還要使用集合而不使用JavaScript內置的數組? 首先,通過擴展Backbone.Collection的方式來創建對象有利於“文檔化”你的代碼。例如,在下面的例子中,集合類 Team 定義了 Stooge 作為它的數據模型類型,所以當你書寫代碼時,你就知道backbone在處理該集合時是依照怎樣的數據類型結構。 當然,使用集合更重要的原因是,集合提供了一套強大的命令讓你 獲取/操縱 里面的內容,因此你不需要自己寫對應的方法。

var Stooge = Backbone.Model.extend({
    defaults: {
        'name': '',
        'power': ''
    }
});
var Team = BackBone.Collection.extend({
    model: Stooge
});
var larry = new Stooge({
    name: 'Larry',
    power: 'Baldness'
});
var moe = new Stooge({
    name: 'Moe',
    power: 'All Powers'
});
var curly = new Stooge({
    name: 'Curly',
    power: 'Hair'
});
var threeStooges = new Team([larry, curly, moe]);

當集合中包含的“模型”發生數據變化時,集合會激發相應的事件,因此你可以捕獲那些事件從而完成視圖上的更新。

 

同步 Sync
在backbone中,使用 Sync 類(Backbone.sync)完成與服務器端的數據模型的讀取與存儲。 通常情況下,你可以用 jQuery的 .ajax 方法來發送和接收 JSON 格式的數據。如果想采用不同的持久化方案,比如 WebSockets, XML, 或 Local Storage,我們可以重載該函數。

Backbone.sync 的語法為 sync(method, model, [optional callback])。

       method – CRUD 方法 ("create", "read", "update", 或 "delete")    create對應POST,read 對應GET。
       model – 要被保存的模型(或要被讀取的集合)

與Sync配套使用最常見的方法是 model.save([attributes], [options])。 model.save 通過委托 Backbone.sync 保存數據模型到服務器端。 attributes 散列表 (在 set) 應當包含想要改變的屬性,不涉及的鍵不會被修改。 如果模型含有 validate 方法,並且驗證失敗,模型不會保存。 如果模型在服務器端不存在, save將采用 "create" (HTTP POST) 方法, 如果模型已經在服務器存在,保存將采用 "update" (HTTP PUT) 方法.

在下面的示例,我們在client端生成一個圖書的數據模型,並把它sync到服務器端

Backbone.sync = function (method, model) {
    alert(method + ": " + JSON.stringify(model));
    model.id = 1;
};

var book = new Backbone.Model({
    title: "The Rough Riders",
    author: "Theodore Roosevelt"
});

book.save(); 由於模型在服務器端沒有,所以這一次調用,sync會使用 "create" 請求

book.save({
    author: "Teddy"
}); 這一次服務器端有了,所以sync會使用 "update"請求。注意,這只更新了模型的部分屬性。沒有涉及的屬性在服務器端不會被修改。

 

路由 Router

web應用程序通常需要為應用的重要位置提供可鏈接,可收藏,可分享的 URLs。 越來越多的web程序開始使用 錨點(hash)片段(#page)來提供這種可收藏,可分享的鏈接。 同時隨着 History API 的到來,錨點已經可以用於處理標准 URLs (/page)。 Backbone.Router 為客戶端路由提供了許多方法,並能連接到指定的動作(actions)和事件(events)。 對於不支持 History API 的舊瀏覽器,路由提供了優雅的回調函數並可以透明的進行 URL 片段的轉換。

看上去,在 Backbone 里似乎缺少了 Controller 類型,實際上,每一個“路由”都可以被看作是從 Controller 上擴展的。 傳統意義上的 Controller 是有問題的,因為它融合了兩種功能,第一就是根據 View 來控制 Model,第二就是控制用戶在不同的 View 之間切換。而 Backbone 通過把 Router 單獨剝離出來,就優雅地分離了 用戶界面 和 操縱Model。

那為什么不給 controller 建立單獨的類呢?根據MVC定義,Controller類應該是你程序主干邏輯的實現。在backbone里,由於程序的具體處理細節都從 controller 層分離出去了(比如 在Model內部處理數據,由Router處理用戶request等等),因此你不太需要單獨的一個 Controller 類來處理程序的主干邏輯。

下面這個例子顯示了如何設置一個路由,該路由需要處理的URL諸如 #/certificates/123 或者 #/certificates/mycertificatename,然后該路由依據certificateID或者certificatename 新建一個 View (new CertificateView)。 下面這個簡單的代碼並沒有依據 ID 處理CertificateView,但是實際開發中,你可能需要依據ID來 讀取/處理 模型。

var MyRouter = Backbone.Router.extend({
    routes: {
        "/certificates/:id": "getCertificate",
    },
    getCertificate: function (id) {
        new CertificateView({
            el: $("#certificate")
        });
    }
});
var router = new MyRouter;
Backbone.history.start();

 

參考資料:

Backbone中文文檔  http://www.csser.com/tools/backbone/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM