什麼是 Backbone.js

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

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

 

模型 Modelhtml

運用Backbone框架的程序的核心是「模型」。多數狀況下,「模型」表明了數據庫中存儲的一種對象結構。java

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

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

複製代碼
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/

相關文章
相關標籤/搜索