本文緊接第二部分:《BackboneJS架構的技巧及模式(3)》
作者:chszs,轉載需註明。部落客頁:http://blog.csdn.net/chszs
八、PUT需要一個ID屬性
這一條也經常會對那些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 attributevar model = new Model({ hello: <%= @world %>});// or to have jsonvar model = new Model(<%= @hello_world.to_json %>);
應用此模式可以通過“立即”渲染頁面,以改善你網站的搜尋引擎排名,而且它也可通過限制應用初始化HTTP請求的方式,大幅縮短應用啟動與運行所需的時間。
十、處理失敗的模型屬性驗證
很多時候,你會想知道是哪個模型屬性驗證失敗了。例如,如果你有一個極其複雜的表單欄位,你或許想知道哪一個模型屬性驗證失敗了,以便於你可以將該屬性對應的輸入欄位進行高亮顯示。不幸的是,警告視圖到底是哪個模型屬性驗證失敗並沒有直接整合於Backbone.js,但是你可以用其他不同的模式去處理這個問題。
1、返回一個錯誤對象
一個通知視圖是哪個模型屬性驗證失敗的模式是,返回一個對象,其中包含某種標誌,它詳細的記錄了驗證失敗的屬性,就像下面這樣:
// Inside your modelvalidate: function(attrs) { var errors = []; if(attrs.a < 0) { errors.push({ 'message': 'Form field a is messed up!', 'class': 'a' }); } if(attrs.b < 0) { errors.push({ 'message': 'Form field b is messed up!', 'class': 'b' }); } if(errors.length) { return errors; }}// Inside your viewthis.model.on('invalid’, function(model, errors) { _.each(errors, function(error, i) { $(‘.’ + error.class).addClass('error'); alert(error.message); });});
此模式的優點在於,你可以在一個地方處理所有無效的訊息。缺點在於,如果你用不同的invalid屬性處理無效屬性,那麼你的invalid方法裡面可能會有非常龐大的switch語句或if語句。
2、廣播自訂的Error事件
我的朋友Derick Bailey推薦了一個可替代的模式,就是為每個模型屬性觸發自訂的errors事件。它允許你的視圖能夠針對單獨的屬性綁定特定的error事件:
// Inside your modelvalidate: function(attrs) { if(attrs.a < 0) { this.trigger(‘invalid:a’, 'Form field a is messed up!', this); } if(attrs.b < 0) { this.trigger(‘invalid:b’, 'Form field b is messed up!', this); }}// Inside your viewthis.model.on('invalid:a’, function(error) { $(‘a’).addClass('error'); alert(error);});this.model.on('invalid:b’, function(error) { $(‘b’).addClass('error'); alert(error);});
此模式的優點在於,你的視圖綁定的error類型是清晰的,並且對於每一種error屬性,可以有特定的指令,它可以使得視圖方面的代碼很清晰,使之更易於維護。此模式的缺點在於,視圖的代碼可能會變得極為膨脹。
這兩種模式都各有利弊,你應該考慮清楚哪個模式對你的應用案例是最優的。如果你按照同樣的方式處理所有失敗的驗證,那麼第一個方法可能是最好的;如果你對每個模型屬性有特定的UI變化,那麼後一種方法會更好。
十一、HTTP狀態代碼200觸發錯誤
如果終端的模型或集合收到了無效的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.'; } }); }});
1、在單頁應用中緩衝對象
當談論單頁應用時,緩衝對象的模式會經常用到!下面的例子簡明扼要:
// Inside a routerinitialize: 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開發過應用,也許你會問你自己,“ 我要重取資料該怎麼做?”你可以每次在如下路徑中觸發後重取資料:
// Inside a routerinitialize: 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 }); this.cached.model.fetch();}
當應用程式從端點(比如收件匣)必須取回最新的資料,上面的模式就可以工作。當然,如果待取回的資料時依賴於應用程式的狀態(假設這個狀態是通過URL和參數來決定的),那麼即使應用程式的狀態為發生改變,也應該重新取回資料。更好的解決方案是只在應用程式的參數發生變化時重新取回資料:
// Inside a routerinitialize: function() { this.cached = { view: undefined, model: undefined }},index: function(parameter) { this.cached.model = this.cached.model || new Model({ parameter:parameter }); this.cached.model.set('parameter', parameter); this.cached.view = this.cached.view || new View({ model: this.cached.model });}// Inside of the modelinitialize: function() { this.on("change:parameter", this.fetchData, this);}
十四、JSDoc函數和Backbone.js類
我是文檔注釋和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的項目例子,這裡有:
https://github.com/homeaway/calendar-widget
十五、測試驅動的開發實踐
我認為如果你使用Backbone.js開發項目,那麼你應遵循測試驅動開發TDD。我第一次用Jasmine.js建立模型和集合時遵循TDD進行單元測試,但失敗了。一旦寫下單元測試並且失敗,我會對整個模型和集合進行重寫。
通過這一點,我的所有Jasmine測試都通過了,而且我有信心我的模型及集合會和我期望的一樣工作。自從我遵循TDD,我的視圖層非常容易編寫而且非常簡單。當你開始用TDD時,你的開發速度當然會很慢;但是一但你的腦海裡一直想著TDD,你的編程效率和品質會顯著提高。