- 翻譯:一回
- 日期:2011-8-16
- 反饋:xianlihua$gmail.com [$ -> @]
- 特別鳴謝:CSSer
本手冊可自由轉載,但須注明譯者及來源,尊重他人勞動,共建和諧的學習與研究氛圍,從你我做起。
簡單術語翻譯對照:散列表(hash) 模型(model) 視圖(view) 集合(collection) 回調函數(callback) 綁定(bind)
Backbone 為復雜Javascript應用程序提供模型(models)、集合(collections)、視圖(views)的結構。其中模型用於綁定鍵值數據和自定義事件;集合附有可枚舉函數的豐富API; 視圖可以聲明事件處理函數,並通過RESRful JSON接口連接到應用程序。
Backbone項目 托管在Github
Backbone是 DocumentCloud 的一個開源組件.
下載和依賴
一回 翻譯的為 0.5.3 版本,下載請前往 Backbone官網 。
Backbone.js 唯一重度依賴 Underscore.js. 對於 RESTful , history 的支持依賴於 Backbone.Router , DOM 處理依賴於Backbone.View , json2.js, 和 jQuery ( > 1.4.2) 或 Zepto 之一.
簡介
當我們開發含有大量Javascript的web應用程序時,首先你需要做的事情之一便是停止向DOM對象附加數據。 通過復雜多變的jQuery選擇符和回調函數創建Javascript應用程序,包括在HTML UI,Javascript邏輯和數據之間保持同步,都不復雜。 但對富客戶端應用來說,良好的架構通常是有很多益處的。
Backbone將數據呈現為 模型, 你可以創建模型、對模型進行驗證和銷毀,甚至將它保存到服務器。 當UI的變化引起模型屬性改變時,模型會觸發"change"事件; 所有顯示模型數據的 視圖 會接收到該事件的通知,繼而視圖重新渲染。 你無需查找DOM來搜索指定id的元素去手動更新HTML。 — 當模型改變了,視圖便會自動變化。
Backbone.Events
Events 是一個可以被mix到任意對象的模塊,它擁有讓對象綁定和觸發自定義事件的能力。 事件在被綁定之前是不需要事先聲明的,還可以攜帶參數。我們通過一個例子來看:
var object = {}; _.extend(object, Backbone.Events); object.bind("alert", function(msg) { alert("Triggered " + msg); }); object.trigger("alert", "www.csser.com");
bindobject.bind(event, callback, [context])
綁定 callback 函數到 object 對象。 當事件觸發時執行回調函數 callback 。如果一個頁面中有大量不同的事件,按照慣例使用冒號指定命名空間: "poll:start", 或 "change:selection"
當 callback 執行時提供第三個可選參數,可以為 this 指定上下文: model.bind('change', this.render, this)
綁定到特殊事件 "all" 的回調函數會在任意事件發生時被觸發,其第一個參數為事件的名稱。 例如,將一個對象的所有事件代理到另一對象:
proxy.bind("all", function(eventName) { object.trigger(eventName); });
unbindobject.unbind([event], [callback])
從 object 對象移除先前綁定的 callback 函數。如果不指定第二個參數,所有 event 事件綁定的回調函數都被移除。 如果第一個參數也不指定,對象所綁定的所有回調函數都將被移除。
object.unbind("change", onChange); // 只移除onChange回調函數 object.unbind("change"); // 移除所有 "change" 回調函數 object.unbind(); // 移除對象的所有回調函數
triggerobject.trigger(event, [*args])
觸發 event 事件的回調函數。后續傳入 trigger 的參數會被依次傳入事件回調函數。
Backbone.Model
模型 是所有 Javascript 應用程序的核心,包括交互數據及相關的大量邏輯: 轉換、驗證、計算屬性和訪問控制。你可以用特定的方法擴展 Backbone.Model , 模型 也提供了一組基本的管理變化的功能。
下面的示例演示了如何定義一個模型,包括自定義方法、設置屬性、以及觸發該屬性變化的事件。
var Sidebar = Backbone.Model.extend({ promptColor: function() { var cssColor = prompt("請輸入一個CSS顏色值:"); this.set({color: cssColor}); } }); window.sidebar = new Sidebar; sidebar.bind('change:color', function(model, color) { $('#sidebar').css({background: color}); }); sidebar.set({color: 'white'}); sidebar.promptColor();
extendBackbone.Model.extend(properties, [classProperties])
要創建自己的 模型 類,你可以擴展 Backbone.Model 並提供實例 屬性 , 以及可選的可以直接注冊到構造函數的 類屬性 (classProperties)。
extend 可以正確的設置原型鏈,因此通過 extend 創建的子類 (subclasses) 也可以被深度擴展。
var Note = Backbone.Model.extend({ initialize: function() { ... }, author: function() { ... }, coordinates: function() { ... }, allowedToEdit: function(account) { return true; } }); var PrivateNote = Note.extend({ allowedToEdit: function(account) { return account.owns(this); } });
父類 的簡述:Javascript沒有提供一種直接調用父類的方式, 如果你要重載原型鏈中上層定義的同名函數,如 set,或 save , 並且你想調用父對象的實現,這時需要明確的調用它,類似這樣:
var CSSercom = Backbone.Model.extend({ set: function(attributes, options) { Backbone.Model.prototype.set.call(this, attributes, options); ... } });
constructor / initializenew Model([attributes])
當創建模型實例時,可以傳入 屬性 初始值,這些值會被 set 到模型。 如果定義了 initialize 函數,該函數會在模型創建后執行。
new Site({ title: "CSSer, 關注web前后端技術", author: "一回" });
getmodel.get(attribute)
從模型獲取當前屬性值,比如:csser.get("title")
setmodel.set(attributes, [options])
向模型設置一個或多個散列屬性。 如果任何一個屬性改變了模型的狀態,在不傳入 {silent: true} 選項參數的情況下,會觸發 "change" 事件。 可以綁定事件到某個屬性,例如:change:title,及 change:content。
csser.set({title: "CSSer", content: "http://www.csser.com"});
如果模型擁有 validate 方法, 那么屬性驗證會在 set 之前執行,如果驗證失敗,模型不會發生變化,這時 set 會返回false。 也可以在選項中傳入 error 回調函數,此時驗證失敗時會執行它而不觸發 "error" 事件。
escapemodel.escape(attribute)
與 get 類似, 但返回模型屬性值的 HTML 轉義后的版本。 如果將數據從模型插入 HTML,使用 escape 取數據可以避免XSS 攻擊.
var hacker = new Backbone.Model({ name: "<script>alert('xss')</script>" }); alert(hacker.escape('name'));
hasmodel.has(attribute)
屬性值為非 null 或非 undefined 時返回 true
if (note.has("title")) { ... }
unsetmodel.unset(attribute, [options])
從內部屬性散列表中刪除指定屬性。 如果未設置 silent 選項,會觸發 "change" 事件。
clearmodel.clear([options])
從模型中刪除所有屬性。 如果未設置 silent 選項,會觸發 "change" 事件。
idmodel.id
模型的特殊屬性, id 可以是任意字符串(整型 id 或 UUID)。 在屬性中設置的 id 會被直接拷貝到模型屬性上。 我們可以從集合(collections)中通過 id 獲取模型,另外 id 通常用於生成模型的 URLs。
cidmodel.cid
模型的特殊屬性,cid 或客戶 id 是當所有模型創建時自動產生的唯一標識符。 客戶 ids 在模型尚未保存到服務器之前便存在,此時模型可能仍不具有最終的 id, 客戶 ids 的形式為:c1, c2, c3 ...
attributesmodel.attributes
attributes 屬性是包含模型狀態的內部散列表。 建議采用 set 更新屬性而不要直接修改。 如要獲取模型屬性的副本, 用toJSON 取而代之。
defaultsmodel.defaults or model.defaults()
defaults 散列(或函數)用於為模型指定默認屬性。 創建模型實例時,任何未指定的屬性會被設置為其默認值。
var Meal = Backbone.Model.extend({ defaults: { "appetizer": "caesar salad", "entree": "ravioli", "dessert": "cheesecake" } }); alert("Dessert will be " + (new Meal).get('dessert'));
需要提醒的是,在 Javascript 中,對象是按引用傳值的,因此包含對象作為默認值,它會被所有實例共享。
toJSONmodel.toJSON()
返回模型 attributes 副本的 JSON 字符串化形式。 它可用於模型的持久化、序列化,或者傳遞到視圖前的擴充。 該方法的名稱有點混亂,因為它事實上並不返回 JSON 字符串,但 JavaScript API for JSON.stringify 可以實現。
var artist = new Backbone.Model({ firstName: "立華", lastName: "咸" }); artist.set({birthday: "December 13, 1979"}); alert(JSON.stringify(artist));
fetchmodel.fetch([options])
從服務器重置模型狀態。這對模型尚未填充數據,或者服務器端已有最新狀態的情況很有用處。 如果服務器端狀態與當前屬性不同,則觸發 "change" 事件。 選項的散列表參數接受 success 和 error 回調函數, 回調函數中可以傳入(model,response) 作為參數。
// 每隔 10 秒從服務器拉取數據以保持頻道模型是最新的 setInterval(function() { channel.fetch(); }, 10000);
savemodel.save([attributes], [options])
通過委托 Backbone.sync 保存模型到數據庫(或可替代的持久層)。 attributes 散列表 (在 set) 應當包含想要改變的屬性,不涉及的鍵不會被修改。 如果模型含有 validate 方法,並且驗證失敗,模型不會保存。 如果模型 isNew, 保存將采用"create" (HTTP POST) 方法, 如果模型已經在服務器存在,保存將采用 "update" (HTTP PUT) 方法.
在下面的示例,注意我們是如何在模型初次保存時接收到 "create" 請求,第二次接收到 "update" 請求的。
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(); book.save({author: "Teddy"});
save 支持在選項散列表中傳入 success 和 error 回調函數, 回調函數支持傳入 (model, response) 作為參數。 如果模型擁有 validate 方法並且驗證失敗,error 回調函數會執行。 如果服務端驗證失敗,返回非 200 的 HTTP 響應碼,將產生文本或 JSON 的錯誤內容。
book.save({author: "F.D.R."}, {error: function(){ ... }});
destroymodel.destroy([options])
通過委托 HTTP DELETE 請求到 Backbone.sync 銷毀服務器上的模型. 接受 success 和 error 回調函數作為選項散列表參數。 將在模型上觸發 "destroy" 事件,該事件可以通過任意包含它的集合向上冒泡。
book.destroy({success: function(model, response) { ... }});
validatemodel.validate(attributes)
該方法是未定義的,如果有在Javascript執行的需要,建議用自定義的驗證邏輯重載它。 validate 會在 set 和 save 之前調用,並傳入待更新的屬性。 如果模型和屬性通過驗證,不返回任何值; 如果屬性不合法,返回一個可選擇的錯誤。該錯誤可以是簡單的用於顯示的字符串錯誤信息, 或者是一個可以描述錯誤詳細的 error 對象。 如果 validate 返回錯誤,set 和 save 將不會執行。 失敗的驗證會觸發一個 "error"事件。
var Chapter = Backbone.Model.extend({ validate: function(attrs) { if (attrs.end < attrs.start) { return "can't end before it starts"; } } }); var one = new Chapter({ title : "Chapter One: The Beginning" }); one.bind("error", function(model, error) { alert(model.get("title") + " " + error); }); one.set({ start: 15, end: 10 });
"error" 事件對模型和集合級別提供粗粒度的錯誤信息很有幫助, 但如果想設計更好的處理錯誤的特定視圖,可以直接傳入 error 回調函數重載事件。
account.set({access: "unlimited"}, { error: function(model, error) { alert(error); } });
urlmodel.url()
返回模型資源在服務器上位置的相對 URL 。 如果模型放在其它地方,可通過合理的邏輯重載該方法。 生成 URLs 的形式為:"/[collection.url]/[id]", 如果模型不是集合的一部分,則 URLs 形式為:"/[urlRoot]/id"。
由於是委托到 Collection#url 來生成 URL, 所以首先需要確認它是否定義過,或者所有模型共享一個通用根 URL 時,是否存在 urlRoot 屬性。 例如,一個 id 為 101 的模型,存儲在 url 為 "/documents/7/notes" 的 Backbone.Collection中, 那么該模型的 URL 為:"/documents/7/notes/101"
urlRootmodel.urlRoot
如果使用的集合外部的模型,通過指定 urlRoot 來設置生成基於模型 id 的 URLs 的默認 url 函數。 "/[urlRoot]/id"
var Book = Backbone.Model.extend({urlRoot : '/books'}); var solaris = new Book({id: "1083-lem-solaris"}); alert(solaris.url());
parsemodel.parse(response)
parse 會在通過 fetch 從服務器返回模型數據,以及 save 時執行。 傳入本函數的為原始 response 對象,並且應當返回可以 set 到模型的屬性散列表。 默認實現是自動進行的,僅簡單傳入 JSON 響應。 如果需要使用已存在的 API,或者更好的命名空間響應,可以重載它。
如果使用的 Rails 后端,需要注意 Rails's 默認的 to_json 實現已經包含了命名空間之下的模型屬性。 對於無縫的后端集成環境禁用這種行為:
ActiveRecord::Base.include_root_in_json = false
clonemodel.clone()
返回與模型屬性一致的新的實例。
isNewmodel.isNew()
模型是否已經保存到服務器。 如果模型尚無 id,則被視為新的。
changemodel.change()
手動觸發 "change" 事件。 如果已經在 set 函數傳入選項參數 {silent: true} , 當所有操作結束時,可以手動調用model.change() 。
hasChangedmodel.hasChanged([attribute])
標識模型從上次 "change" 事件發生后是否改變過。 如果傳入 attribute ,當指定屬性改變后返回 true。
注意,本方法以及接下來 change 相關的方法,僅對 "change" 事件發生有效。
book.bind("change", function() { if (book.hasChanged("title")) { ... } });
changedAttributesmodel.changedAttributes([attributes])
僅獲取模型屬性已改變的散列表。 或者也可以傳入外來的 attributes 散列,返回該散列與模型不同的屬性。 一般用於指出視圖的哪個部分已被更新,或者確定哪些需要與服務器進行同步。
previousmodel.previous(attribute)
在 "change" 事件發生的過程中,本方法可被用於獲取已改變屬性的舊值。
var bill = new Backbone.Model({ name: "二回" }); bill.bind("change:name", function(model, name) { alert("名字已從 " + bill.previous("name") + " 改為 " + name); }); bill.set({name : "一回"});
previousAttributesmodel.previousAttributes()
返回模型的上一個屬性散列的副本。一般用於獲取模型的不同版本之間的區別,或者當發生錯誤時回滾模型狀態。
Backbone.Collection
集合是模型的有序組合,我們可以在集合上綁定 "change" 事件,從而當集合中的模型發生變化時獲得通知,集合也可以監聽 "add" 和 “remove" 事件, 從服務器更新,並能使用 Underscore.js 提供的方法
集合中的模型觸發的任何事件都可以在集合身上直接觸發,所以我們可以監聽集合中模型的變化:Documents.bind("change:selected", ...)
extendBackbone.Collection.extend(properties, [classProperties])
通過擴展 Backbone.Collection 創建一個 Collection 類。實例屬性參數 properties 以及 類屬性參數classProperties 會被直接注冊到集合的構造函數。
modelcollection.model
指定集合的模型類。可以傳入原始屬性對象(和數組)來 add,create,以及 reset,傳入的屬性會被自動轉換為適合的模型類型。
var Library = Backbone.Collection.extend({ model: Book });
constructor / initializenew Collection([models], [options])
當創建集合時,你可以選擇傳入初始的 模型 數組。集合的 comparator 函數也可以作為選項傳入。 如果定義了 initialize函數,會在集合創建時被調用。
var tabs = new TabSet([tab1, tab2, tab3]);
modelscollection.models
訪問集合中模型的原始值。通常我們使用 get,at,或 Underscore方法 訪問模型對象,但偶爾也需要直接訪問。
toJSONcollection.toJSON()
返回集合中包含的每個模型對象的數組。可用於集合的序列化和持久化。本方法名稱容易引起混淆,因為它與JavaScript's JSON API 命名相同.
var collection = new Backbone.Collection([ {name: "Tim", age: 5}, {name: "Ida", age: 26}, {name: "Rob", age: 55} ]); alert(JSON.stringify(collection));
Underscore 方法 (26)
Backbone 代理了 Underscore.js 從而為 Backbone.Collection 提供了26個迭代函數。這里沒有列出這些函數的使用方法,你可以點擊鏈接前往查看:
- forEach (each)
- map
- reduce (foldl, inject)
- reduceRight (foldr)
- find (detect)
- filter (select)
- reject
- every (all)
- some (any)
- include
- invoke
- max
- min
- sortBy
- groupBy
- sortedIndex
- toArray
- size
- first
- rest
- last
- without
- indexOf
- lastIndexOf
- isEmpty
- chain
Books.each(function(book) { book.publish(); }); var titles = Books.map(function(book) { return book.get("title"); }); var publishedBooks = Books.filter(function(book) { return book.get("published") === true; }); var alphabetical = Books.sortBy(function(book) { return book.author.get("name").toLowerCase(); });
addcollection.add(models, [options])
向集合中增加模型(或模型數組)。默認會觸發 "add" 事件,可以傳入 {silent : true} 關閉。 如果定義了 模型 屬性,也可以傳入原始的屬性對象讓其看起來像一個模型實例。 傳入 {at: index} 可以將模型插入集合中特定的位置。
var ships = new Backbone.Collection; ships.bind("add", function(ship) { alert("Ahoy " + ship.get("name") + "!"); }); ships.add([ {name: "Flying Dutchman"}, {name: "Black Pearl"} ]);
removecollection.remove(models, [options])
從集合中刪除模型(或模型數組)。會觸發 "remove" 事件,同樣可以使用 silent 關閉。
getcollection.get(id)
返回集合中 id 為 id 的模型。
var book = Library.get(110);
getByCidcollection.getByCid(cid)
通過指定客戶id返回集合中的模型。客戶id是指模型創建時自動生成的 .cid 屬性。在模型尚未保存到服務器時其還沒有id值,所以通過cid獲取模型很有用處。
atcollection.at(index)
返回集合中指定索引的模型對象。不論你是否對模型進行了重新排序, at 始終返回其在集合中插入時的索引值。
lengthcollection.length
與數組類似,集合擁有 length 屬性,返回該集合擁有的模型數量。
comparatorcollection.comparator
默認情況下,集合沒有聲明 comparator 函數。如果定義了該函數,集合中的模型會按照指定的算法進行排序。 換言之,模型被增加的同時會插入適合的位置。Comparator接收模型作為參數,返回數值或字符串作為相對其它模型的排序依據。
注意即使下面例子中的章節是后加入集合中的,但它們都會遵循正確的排序:
var Chapter = Backbone.Model; var chapters = new Backbone.Collection; chapters.comparator = function(chapter) { return chapter.get("page"); }; chapters.add(new Chapter({page: 9, title: "The End"})); chapters.add(new Chapter({page: 5, title: "The www.csser.com"})); chapters.add(new Chapter({page: 1, title: "The Beginning"})); alert(chapters.pluck('title'));
說明:comparator 函數與 Javascript 的 "sort" 並不相同,后者必須返回 0, 1, 或 -1, 前者則更像 sortBy — 一個更友好的API。
sortcollection.sort([options])
強制對集合進行重排序。一般情況下不需要調用本函數,因為 comparator 函數會實時排序。 如果不指定 {silent: true} ,調用 sort 會觸發集合的 "reset" 事件。
pluckcollection.pluck(attribute)
從集合中的每個模型拉取 attribute。等價於調用 map,並從迭代器中返回單個屬性。
var stooges = new Backbone.Collection([ new Backbone.Model({name: "Curly"}), new Backbone.Model({name: "Larry"}), new Backbone.Model({name: "Moe"}) ]); var names = stooges.pluck("name"); alert(JSON.stringify(names));
urlcollection.url or collection.url()
設置 url 屬性(或函數)以指定集合對應的服務器位置。集合內的模型使用 url 構造自身的 URLs。
var Notes = Backbone.Collection.extend({ url: '/notes' }); // 或者,更復雜一些的方式: var Notes = Backbone.Collection.extend({ url: function() { return this.document.url() + '/notes'; } });
parsecollection.parse(response)
每一次調用 fetch 從服務器拉取集合的模型數據時,parse都會被調用。 本函數接收原始 response 對象,返回可以 add到集合的模型屬性數組。 默認實現是無需操作的,只需簡單傳入服務端返回的JSON對象。 如果需要處理遺留API,或者在返回數據定義自己的命名空間,可以重寫本函數。
var Tweets = Backbone.Collection.extend({ // Twitter 搜索 API 在 "result" 鍵下返回 tweets parse: function(response) { return response.results; } });
fetchcollection.fetch([options])
從服務器拉取集合的默認模型,成功接收數據后會重置(reset)集合。 options 支持 success 和 error 回調函數,回調函數接收 (collection, response) 作為參數。 可以委托 Backbone.sync 在隨后處理個性化需求。 處理 fetch 請求的服務器應當返回模型的 JSON 數組。
Backbone.sync = function(method, model) { alert(method + ": " + model.url); }; var Accounts = new Backbone.Collection; Accounts.url = '/accounts'; Accounts.fetch();
如果希望向當前集合追加模型數據而不是替換,傳入 {add: true} 作為 fetch 的參數。
fetch 的參數可以支持直接傳入 jQuery.ajax 作為參數,所以拉取指定頁碼的集合數據可以這樣寫:。Documents.fetch({data: {page: 3}})
不建議在頁面加載完畢時利用 fetch 拉取並填充集合數據 — 所有頁面初始數據應當在 bootstrapped 時已經就緒。 fetch適用於惰性加載不需立刻展現的模型數據。
resetcollection.reset(models, [options])
每次一個的向集合做增刪操作已經很好了,但有時會有很多的模型變化以至於需要對集合做大批量的更新操作。 利用reset 可將集合替換為新的模型(或鍵值對象),結束后觸發 "reset" 事件。 傳入 {silent: true} 忽略 "reset" 事件的觸發。 不傳入任何參數將清空整個集合。
這里有一個在頁面加載完畢后 reset 初始啟動集合的例子:
<script> Accounts.reset(<%= @csser.to_json %>); </script>
createcollection.create(attributes, [options])
在集合中創建一個模型。 等價於用鍵值對象實例一個模型,然后將模型保存到服務器,保存成功后將模型增加到集合中。 如果驗證失敗會阻止模型創建,返回 false,否則返回該模型。 為了能正常運行,需要在集合中設置 model 屬性。create 方法接收鍵值對象或者已經存在尚未保存的模型對象作為參數。
var Library = Backbone.Collection.extend({ model: Book }); var NYPL = new Library; var othello = NYPL.create({ title: "Backbone.js API 中文手冊", author: "一回(www.csser.com)" });
Backbone.Router
web應用程序通常需要為應用的重要位置提供可鏈接,可收藏,可分享的 URLs。 直到最近, 貓點(hash)片段(#page)可以被用來提供這種鏈接, 同時隨着 History API 的到來,貓點已經可以用於處理標准 URLs (/page)。Backbone.Router 為客戶端路由提供了許多方法,並能連接到指定的動作(actions)和事件(events)。 對於不支持 History API 的舊瀏覽器,路由提供了優雅的回調函數並可以透明的進行 URL 片段的轉換。
頁面加載期間,當應用已經創建了所有的路由,需要調用 Backbone.history.start(),或Backbone.history.start({pushState : true}) 來確保驅動初始化 URL 的路由。
extendBackbone.Router.extend(properties, [classProperties])
創建一個自定義的路由類。 可以通過 routes 定義路由動作鍵值對,當匹配了 URL 片段便執行定義的動作。
var Workspace = Backbone.Router.extend({ routes: { "help": "help", // #help "search/:query": "search", // #search/kiwis "search/:query/p:page": "search" // #search/kiwis/p7 }, help: function() { ... }, search: function(query, page) { ... } });
routesrouter.routes
routes 將帶參數的 URLs 映射到路由實例的方法上,這與 視圖 的 事件鍵值對 非常類似。 路由可以包含參數,:param,它在斜線之間匹配 URL 組件。 路由也支持通配符,*splat,可以匹配多個 URL 組件。
舉個例子,路由 "search/:query/p:page" 能匹配 #search/obama/p2 , 這里傳入了 "obama" 和 "2" 到路由對應的動作中去了。 "file/*path 路由可以匹配 #file/nested/folder/file.txt,這時傳入動作的參數為"nested/folder/file.txt"。
當訪問者點擊瀏覽器后退按鈕,或者輸入 URL ,如果匹配一個路由,此時會觸發一個基於動作名稱的 事件, 其它對象可以監聽這個路由並接收到通知。 下面的示例中,用戶訪問 #help/uploading 將從路由中觸發 route:help 事件。
routes: { "help/:page": "help", "download/*path": "download", "folder/:name": "openFolder", "folder/:name-:mode": "openFolder" }
router.bind("route:help", function(page) { ... });
constructor / initializenew Router([options])
實例化一個路由對象,你可以直接傳入 routes 鍵值對象作為參數。 如果定義該參數, 它們將被傳入 initialize 構造函數中初始化。
routerouter.route(route, name, callback)
為路由對象手動創建路由,route 參數可以是 路由字符串 或 正則表達式。 每個捕捉到的被傳入的路由或正則表達式,都將作為參數傳入回調函數(callback)。 一旦路由匹配,name 參數會觸發 "route:name" 事件。
initialize: function(options) { // 匹配 #page/10, 傳入回調函數 "10" this.route("page/:number", "page", function(number){ ... }); // 匹配 /csser.com/b/c/open, 傳入回調函數 "csser.com/b/c" this.route(/^(.*?)\/open$/, "open", function(id){ ... }); }
openPage: function(pageNumber) { this.document.pages.at(pageNumber).open(); this.navigate("page/" + pageNumber); } # 或者 ... app.navigate("help/troubleshooting", true);
Backbone.history
History 作為全局路由服務用於處理 hashchange 事件或 pushState,匹配適合的路由,並觸發回調函數。 我們不需要自己去做這些事情 — 如果使用帶有鍵值對的 路由,Backbone.history 會被自動創建。
Backbone 會自動判斷瀏覽器對 pushState 的支持,以做內部的選擇。 不支持 pushState 的瀏覽器將會繼續使用基於貓點的 URL 片段, 如果兼容 pushState 的瀏覽器訪問了某個 URL 貓點,將會被透明的轉換為真實的 URL。 注意使用真實的 URLs 需要 web 服務器支持直接渲染那些頁面,因此后端程序也需要做修改。 例如,如果有這樣一個路由/document/100,如果瀏覽器直接訪問它, web 服務器必須能夠處理該頁面。 趨於對搜索引擎爬蟲的兼容,讓服務器完全為該頁面生成靜態 HTML 是非常好的做法 ... 但是如果要做的是一個 web 應用,只需要利用 Javascript 和 Backbone 視圖將服務器返回的 REST 數據渲染就很好了。
startBackbone.history.start([options])
當所有的 路由 創建並設置完畢,調用 Backbone.history.start() 開始監控 hashchange 事件並分配路由。
需要指出的是,如果想在應用中使用 HTML5 支持的 pushState,只需要這樣做:Backbone.history.start({pushState : true}) 。
如果應用不是基於域名的根路徑 /,需要告訴 History 基於什么路徑: Backbone.history.start({pushState: true, root: "/public/search/"})
當執行后,如果某個路由成功匹配當前 URL,Backbone.history.start() 返回 true。 如果沒有定義的路由匹配當前 URL,返回 false。
如果服務器已經渲染了整個頁面,但又不希望開始 History 時觸發初始路由,傳入 silent : true 即可。
$(function(){ new WorkspaceRouter(); new HelpPaneRouter(); Backbone.history.start({pushState: true}); });
Backbone.sync
Backbone.sync 是 Backbone 每次向服務器讀取或保存模型時都要調用執行的函數。 默認情況下,它使用(jQuery/Zepto).ajax 方法發送 RESTful json 請求。 如果想采用不同的持久化方案,比如 WebSockets, XML, 或 Local Storage,我們可以重載該函數。
Backbone.sync 的語法為 sync(method, model, [options])。
- method – CRUD 方法 ("create", "read", "update", 或 "delete")
- model – 要被保存的模型(或要被讀取的集合)
- options – 成功和失敗的回調函數,以及所有 jQuery 請求支持的選項
默認情況下,當 Backbone.sync 發送請求以保存模型時,其屬性會被序列化為 JSON,並以 application/json 的內容類型發送。 當接收到來自服務器的 JSON 響應后,對經過服務器改變的模型進行拆解,然后在客戶端更新。 當 "read"請求從服務器端響應一個集合(Collection#fetch)時,便拆解模型屬性對象的數組。
默認 sync 映射 REST 風格的 CRUD 類似下面這樣:
- create → POST /collection
- read → GET /collection[/id]
- update → PUT /collection/id
- delete → DELETE /collection/id
emulateHTTPBackbone.emulateHTTP = true
老的瀏覽器不支持 Backbone 默認的 REST/HTTP,此時可以開啟 Backbone.emulateHTTP 。 設置該選項將通過 POST 方法偽造 PUT 和 DELETE 請求,此時該請求會向服務器傳入名為 _method 的參數。 設置該選項同時也會向服務器發送 X-HTTP-Method-Override 頭。
Backbone.emulateHTTP = true; model.save(); // POST 到 "/collection/id", 附帶 "_method=PUT" + header.
emulateJSONBackbone.emulateJSON = true
同樣老的瀏覽器也不支持發送 application/json 編碼的請求, 設置 Backbone.emulateJSON = true; 后 JSON 模型會被序列化為 model 參數, 請求會按照 application/x-www-form-urlencoded 的內容類型發送,就像提交表單一樣。
Backbone.View
Backbone 視圖的使用相當方便 — 它不會影響任何的 HTML 或 CSS 代碼,並且可以與任意 Javascript 模板引擎兼容。 基本的做法就是,將界面組織到邏輯視圖,之后是模型,當模型數據發生改變,視圖立刻自動更新,這一切都不需要重繪頁面。 我們再也不必鑽進 JSON 對象中,查找 DOM 元素,手動更新 HTML 了,通過綁定視圖的 render 函數到模型的"change" 事件 — 模型數據會即時的顯示在 UI 中。
extendBackbone.View.extend(properties, [classProperties])
創建自定義的視圖類。 通常我們需要重載 render 函數,聲明 事件, 以及通過 tagName,className,或 id 為視圖指定根元素。
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "document-row", events: { "click .icon": "open", "click .button.edit": "openEditDialog", "click .button.delete": "destroy" }, render: function() { ... } });
constructor / initializenew View([options])
每次實例化一個視圖時,傳入的選項參數會被注冊到 this.options 中以備后用。 這里有多個特殊的選項,如果傳入,則直接注冊到視圖中去: model, collection, el, id, className, 以及 tagName. 如果視圖定義了 initialize 函數,當視圖實例化時該函數便立刻執行。 如果希望創建一個指向 DOM 中已存在的元素的視圖,傳入該元素作為選項: new View({el: existingElement})
var doc = Documents.first(); new DocumentRow({ model: doc, id: "document-row-" + doc.id });
elview.el
所有的視圖都擁有一個 DOM 元素(el 屬性),即使該元素仍未插入頁面中去。 視圖可以在任何時候渲染,然后一次性插入 DOM 中去,這樣能盡量減少 reflows 和 repaints 從而獲得高性能的 UI 渲染。 this.el 可以從視圖的tagName,className,以及 id 屬性創建,如果都未指定,el 會是一個空 div。
如果希望將 el 賦給頁面 DOM 中已經存在的元素,直接設置其值為真實的 DOM 元素或 CSS 選擇符字符串。
var ItemView = Backbone.View.extend({ tagName: 'li' }); var BodyView = Backbone.View.extend({ el: 'body' }); var item = new ItemView(); var body = new BodyView(); alert(item.el + ' ' + body.el);
$ (jQuery 或 Zepto)view.$(selector)
如果頁面中引入了 jQuery 或 Zepto ,每個視圖都將擁有 $ 函數,可以在視圖元素查詢作用域內運行。 如果使用該作用域內的 jQuery 函數,就不需要從列表中指定的元素獲取模型的 ids 這種查詢了,我們可以更多的依賴 HTML class 屬性。 它等價於運行:$(selector, this.el)。
ui.Chapter = Backbone.View.extend({ serialize : function() { return { title: this.$(".title").text(), start: this.$(".start-page").text(), end: this.$(".end-page").text() }; } });
renderview.render()
render 默認實現是沒有操作的。 重載本函數可以實現從模型數據渲染視圖模板,並可用新的 HTML 更新 this.el。 推薦的做法是在 render 函數的末尾 return this 以開啟鏈式調用。
var Bookmark = Backbone.View.extend({ render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; } });
Backbone 並不知道開發者使用何種模板引擎。 render 函數中可以采用拼字符串,或者利用 document.createElement創建 DOM 樹等等。 但還是建議選擇一個好的 Javascript 模板引擎。 Mustache.js, Haml-js, 以及 Eco 都是很好的選擇。 因為 Underscore.js 已經引入頁面了, 所以為了防止 XSS 攻擊帶給數據的安全威脅,_.template 可以使用並是一個很好的選擇。
無論基於什么考慮,都永遠不要在 Javascript 中拼接 HTML 字符串。
removeview.remove()
從 DOM 中移除視圖。它等價與下面的語句: $(view.el).remove();
makeview.make(tagName, [attributes], [content])
借助給定的元素類型(tagName),以及可選的 attributes 和 HTML 內容創建 DOM 元素。 通常用於內部創建初始的view.el。
var view = new Backbone.View; var el = view.make("b", {className: "bold"}, "Bold! "); $("#make-demo").append(el);
delegateEventsdelegateEvents([events])
采用 jQuery 的delegate 函數來為視圖內的 DOM 事件提供回調函數聲明。 如果未傳入 events 對象,使用this.events 作為事件源。 事件對象的書寫格式為 {"event selector" : "callback"}。 省略 selector 則事件被綁定到視圖的根元素(this.el)。 默認情況下,delegateEvents 會在視圖的構造函數內被調用,因此如果有 events 對象,所有的 DOM 事件已經被連接, 並且我們永遠不需要去手動調用本函數。
events 屬性也可以被定義成返回 events 對象的函數,這樣讓我們定義事件,以及實現事件的繼承變得更加方便。
視圖 渲染 期間使用 delegateEvents 相比用 jQuery 向子元素綁定事件有更多優點。 所有注冊的函數在傳遞給 jQuery 之前已被綁定到視圖上,因此當回調函數執行時,this 仍將指向視圖對象。 當 delegateEvents 再次運行,此時或許需要一個不同的 events 對象,所以所有回調函數將被移除,然后重新委托 — 這對模型不同行為也不同的視圖挺有用處。
搜索結果頁面顯示文檔的視圖看起來類似這樣:
var DocumentView = Backbone.View.extend({ events: { "dblclick" : "open", "click .icon.doc" : "select", "contextmenu .icon.doc" : "showMenu", "click .show_notes" : "toggleNotes", "click .title .lock" : "editAccessLevel", "mouseover .title .date" : "showTooltip" }, render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; }, open: function() { window.open(this.model.get("viewer_url")); }, select: function() { this.model.set({selected: true}); }, ... });
Utility Functions
noConflictvar backbone = Backbone.noConflict();
返回 Backbone 對象的原始值。通常用於在第三方網站上引入了多個 Backbone 文件,避免沖突。
var localBackbone = Backbone.noConflict(); var model = localBackbone.Model.extend(...);
終於翻譯完畢,感謝支持CSSer的朋友們的來信,感謝你們給出的翻譯建議,謝謝。