這一條也經常會對那些Backbone.js新手造成困擾。要想在調用.save()的時候讓模型發送一個HTTP PUT請求,你的模型需要有一個ID屬性集。記得HTTP PUT謂詞是設計用做更新的吧,是以發送一PUT請求,你的模型需要有一個ID。在理想情況,你的所有模型都有一名為ID的完美ID屬性,但是你從端點接收到的JSON資料或許并不總是有完美命名的ID。
是以,如果你需要更新一個模型,在儲存前確定模型上帶有ID。Backbone.js從0.5版開始允許使用idAttribute來修改模型ID屬性的名稱,如果端點傳回的不是名為id的ID屬性名時。
如果使用的Backbone.js版本低于0.5,建議你修改模型或集合的parse函數,以便将期望的ID屬性映射到屬性ID。這裡有一個快速上手的例子,說明了應怎樣修改parse函數來做到這一點。我們假設你有一個cars的集合,它的ID屬性名是carID。
parse: function(response) {
_.each(response.cars, function(car, i) {
// map the returned ID of carID to the correct attribute ID
response.cars[i].id = response.cars[i].carID;
});
return response;
},
有時你會發現模型或集合需要在頁面加載時進行初始化。許多關于Backbone.js模式的文章,例如Rico Sta Cruz的“Backbone模式”和Katz的“避免常見的Backbone.js陷阱”,讨論了這種模式。這種模式實作很容易,隻需在頁面中内聯一段腳本,通過你選擇的服務端語言,将單個模型屬性或者JSON形式的資料呈現出來。例如,在Rails語言中,我采用下面的一種方法:
// a single attribute
var model = new Model({
hello:
});
// or to have json
var model = new Model();
應用此模式可以通過“立即”渲染頁面,以改善你網站的搜尋引擎排名,而且它也可通過限制應用初始化HTTP請求的方式,大幅縮短應用啟動與運作所需的時間。
很多時候,你會想知道是哪個模型屬性驗證失敗了。例如,如果你有一個極其複雜的表單字段,你或許想知道哪一個模型屬性驗證失敗了,以便于你可以将該屬性對應的輸入字段進行高亮顯示。不幸的是,警告視圖到底是哪個模型屬性驗證失敗并沒有直接內建于Backbone.js,但是你可以用其他不同的模式去處理這個問題。
一個通知視圖是哪個模型屬性驗證失敗的模式是,傳回一個對象,其中包含某種标志,它詳細的記錄了驗證失敗的屬性,就像下面這樣:
// Inside your model
validate: function(attrs) {
var errors = [];
if(attrs.a
errors.push({
'message': 'Form field a is messed up!',
'class': 'a'
});
}
if(attrs.b
'message': 'Form field b is messed up!',
'class': 'b'
if(errors.length) {
return errors;
}
// Inside your view
this.model.on('invalid’, function(model, errors) {
_.each(errors, function(error, i) {
$(‘.’ + error.class).addClass('error');
alert(error.message);
此模式的優點在于,你可以在一個地方處理所有無效的消息。缺點在于,如果你用不同的invalid屬性處理無效屬性,那麼你的invalid方法裡面可能會有非常龐大的switch語句或if語句。
我的朋友Derick Bailey推薦了一個可替代的模式,就是為每個模型屬性觸發自定義的errors事件。它允許你的視圖能夠針對單獨的屬性綁定特定的error事件:
this.trigger(‘invalid:a’, 'Form field a is messed up!', this);
this.trigger(‘invalid:b’, 'Form field b is messed up!', this);
this.model.on('invalid:a’, function(error) {
$(‘a’).addClass('error');
alert(error);
this.model.on('invalid:b’, function(error) {
$(‘b’).addClass('error');
此模式的優點在于,你的視圖綁定的error類型是清晰的,并且對于每一種error屬性,可以有特定的指令,它可以使得視圖方面的代碼很清晰,使之更易于維護。此模式的缺點在于,視圖的代碼可能會變得極為膨脹。
這兩種模式都各有利弊,你應該考慮清楚哪個模式對你的應用案例是最優的。如果你按照同樣的方式處理所有失敗的驗證,那麼第一個方法可能是最好的;如果你對每個模型屬性有特定的UI變化,那麼後一種方法會更好。
如果終端的模型或集合收到了無效的JSON,盡管終端傳回了HTTP狀态碼200,模型或集合還是會觸發error事件。這種問題經常發生于開發階段在本地模拟JSON資料造成的。那麼,有個好方法是抛出經過 JSON 驗證器驗證了的模拟JSON資料檔案。或者使用IDE的插件來及時捕獲格式錯誤的JSON資料。
這能夠節省開發時間以及建立統一的模式來處理和可視化錯誤消息,而且它可以改善整個的使用者體驗。我之前開發的每一個Backbone.js應用中,我都會建立一個通用的警告視圖:
var AlertView = Backbone.View.extend({
set: function(typeOfError, message) {
var alert = $(‘.in-page-alert’).length ? $(‘.in-page-alert’): $(‘.body-alert’);
alert
.removeClass(‘error success warning’)
.addClass(typeOfError)
.html(message)
.fadeIn()
.delay(5000)
.fadeOut();
上面代碼首先會檢查視圖代碼中是否存在指定in-page-alert視圖的DIV元素。如果沒有,則接着檢視通用的body-alert的DIV元素,它可以在布局中進行聲明。這可以傳遞一緻性的錯誤資訊給使用者,而且一旦你忘記指定in-page-alert視圖的DIV元素,它會有一個有用的回報。下面的模式簡化了怎樣在試圖中處理錯誤資訊:
var alert = new AlertView();
this.model.on('error', function(model, error) {
alert.set('TYPE-OF-ERROR', error);
如果你正在開發一個單頁面應用,切記要更新每個頁面的标題。我寫過一個簡單的插件(Backbone.js Router Title Helper),它以簡單、優雅的格式擴充了 backbone.js router 的功能。它通過一個 Map 對象來控制路由,鍵來代表路由函數的名字,值則映射到頁面的标題。
Backbone.Router = Backbone.Router.extend({
initialize: function(options){
var that = this;
this.on('route', function(router, route, params) {
if(that.titles) {
if(that.titles[router]) document.title = that.titles[router];
else if(that.titles.default) document.title = that.titles.default;
else throw 'Backbone.js Router Title Helper: No title found for route:' + router + ' and no default route specified.';
}
當談論單頁應用時,緩存對象的模式會經常用到!下面的例子簡明扼要:
// Inside a router
initialize: function() {
this.cached = {
view: undefined,
model: undefined
index: function(parameter) {
this.cached.model = this.cached.model || new Model({
parameter: parameter
this.cached.view = this.cached.view || new View({
model: this.cached.model
此模式可以加速你的應用程式,因為你不用重新初始化Backbone.js對象。然而,它會過多地消耗記憶體;是以,緩存的對象應該是在整個應用程式中使用的。如果以前你用過Backbone.js開發過應用,也許你會問你自己,“ 我要重取資料該怎麼做?”你可以每次在如下路徑中觸發後重取資料:
this.cached.model.fetch();
當應用程式從端點(比如收件箱)必須取回最新的資料,上面的模式就可以工作。當然,如果待取回的資料時依賴于應用程式的狀态(假設這個狀态是通過URL和參數來決定的),那麼即使應用程式的狀态為發生改變,也應該重新取回資料。更好的解決方案是隻在應用程式的參數發生變化時重新取回資料:
parameter:parameter
this.cached.model.set('parameter', parameter);
// Inside of the model
this.on("change:parameter", this.fetchData, this);
我是文檔注釋和JSDoc的超級粉絲。我用JSDoc對所有的Backbone類添加了文檔注釋:
var Thing = Backbone.View.extend(/** @lends Thing.prototype */{
/** @class Thing
* @author Phillip Whisenhunt
* @augments Backbone.View
* @contructs Thing object */
initialize() {},
/** Gets data by ID from the thing. If the thing doesn't have data based on the ID, an empty string is returned.
* @param {String} id The id of get data for.
* @return {String} The data. */
getDataById: function(id) {}
如果你使用上面的方法對Backbone類添加文檔注釋,那麼你可以生成優美的文檔,文檔包含了所有的類和函數及參數。確定初始化函數應作為第一個聲明的函數,它可以幫助我們生成JSDoc文檔。如果你想看使用JSDoc的項目例子,這裡有:
<a href="https://github.com/homeaway/calendar-widget" target="_blank">https://github.com/homeaway/calendar-widget</a>
我認為如果你使用Backbone.js開發項目,那麼你應遵循測試驅動開發TDD。我第一次用Jasmine.js建立模型和集合時遵循TDD進行單元測試,但失敗了。一旦寫下單元測試并且失敗,我會對整個模型和集合進行重寫。
通過這一點,我的所有Jasmine測試都通過了,而且我有信心我的模型及集合會和我期望的一樣工作。自從我遵循TDD,我的視圖層非常容易編寫而且非常簡單。當你開始用TDD時,你的開發速度當然會很慢;但是一但你的腦海裡一直想着TDD,你的程式設計效率和品質會顯著提高。