本文緊接第一部分:《BackboneJS框架的技巧及模式(1)》javascript
做者:chszs,轉載需註明。博客主頁:http://blog.csdn.net/chszshtml
在實際項目中,需求是常常變化的,所以,終端返回的JSON數據格式也是如此。若是你的視圖和下層的數據模型是緊耦合的,那麼這是一種痛苦。有鑑於此,我爲全部的對象都建立getter和setter函數。java
此模式使用較廣。若是任何底層的數據結構發生了變化,那麼視圖層無需隨之更新;你會有一個數據訪問點,因此你不太可能忘記作深度複製,編寫的代碼將會更易於維護和調試。此模式的缺點在於它可能會致使模型或集合有一點點膨脹。服務器
下面咱們看一個例子來闡明此模式。假想咱們有一個Hotel模型,它包含了rooms和當前有效的rooms,並且咱們但願能夠經過牀位大小來獲取rooms。數據結構
var Hotel = Backbone.Model.extend({ defaults: { "availableRooms": ["a"], "rooms": { "a": { "size": 1200, "bed": "queen" }, "b": { "size": 900, "bed": "twin" }, "c": { "size": 1100, "bed": "twin" } }, getRooms: function() { $.extend(true, {}, this.get("rooms")); }, getRoomsByBed: function(bed) { return _.where(this.getRooms(), { "bed": bed }); } } });
如今咱們假設明天你要發佈項目,但你發現終端開發者忘記告訴你rooms的數據結構發生了修改。你的代碼如今看起來會像下面這樣: 架構
var Hotel = Backbone.Model.extend({ defaults: { "availableRooms": ["a"], "rooms": [ { "name": "a", "size": 1200, "bed": "queen" }, { "name": "b", "size": 900, "bed": "twin" }, { "name": "c", "size": 1100, "bed": "twin" } ], getRooms: function() { var rooms = $.extend(true, {}, this.get("rooms")), newRooms = {}; // transform rooms from an array back into an object _.each(rooms, function(room) { newRooms[room.name] = { "size": room.size, "bed": room.bed } }); }, getRoomsByBed: function(bed) { return _.where(this.getRooms(), { "bed": bed }); } } });
咱們僅修改了一個函數,以便將Hotel的結構轉變爲應用程序所指望的結構,以便整個應用程序仍然能像咱們所預期的那樣工做。若是這裏沒有getter函數,咱們極可能不得不爲rooms的每一個訪問點進行代碼修改。理想狀況下,你會修改全部函數,以適應新的數據結構,但若是你有項目發佈的時間壓力,此模式能夠爲你節省時間。 框架
順便說一句,此模式既能夠被認爲是Facade裝飾模式,由於它隱藏了建立對象複製的複雜性,也能夠認爲是Bridge橋接模式,由於它能夠用於將數據轉換爲所指望的格式。函數
最佳實踐是對任何對象都使用getters 和setters 函數。this
儘管Backbone.js有模型和集合映射到RESTful端點的規則,有時候你可能會花大量時間查找你想要的存儲在服務器端的數據或集合。spa
有一些關於Backbone.js的文章,描述了此模式。下面讓咱們一塊兒來快速看一個小例子。假設你有一個ul元素列表。
<ul> <li><a href="#" data-id="1">One</a></li> <li><a href="#" data-id="2">Two</a></li> . . . <li><a href="#" data-id="n">n</a></li> </ul>
列表有200項,當使用者點擊列表中的某項時,選擇的項變成被選中狀態:
var Model = Backbone.Model.extend({ defaults: { items: [ { "name": "One", "id": 1 }, { "name": "Two", "id": 2 }, { "name": "Three", "id": 3 } ] } }); var View = Backbone.View.extend({ template: _.template($('#list-template').html()), events: { "#items li a": "setSelectedItem" }, render: function() { $(this.el).html(this.template(this.model.toJSON())); }, setSelectedItem: function(event) { var selectedItem = $(event.currentTarget); // Set all of the items to not have the selected class $('#items li a').removeClass('selected'); selectedItem.addClass('selected'); return false; } }); <script id="list-template" type="template"> <ul id="items"> <% for(i = items.length - 1; i >= 0; i--) { %> <li> <a href="#" data-id="<%= item[i].id %>"><%= item[i].name %></a></li> <% } %></ul> </script>
如今咱們能很容易的判斷被選中的項,而且咱們沒有必要通去遍歷列表。並且,若是列表很是大,遍歷會是一個巨大的開銷。所以,當用戶點擊了列表項後,咱們應該存儲被選擇了的項。
var View = Backbone.View.extend({ initialize: function(options) { // Re-render when the model changes this.model.on('change:items', this.render, this); }, template: _.template($('#list-template').html()), events: { "#items li a": "setSelectedItem" }, render: function() { $(this.el).html(this.template(this.model.toJSON())); }, setSelectedItem: function(event) { var selectedItem = $(event.currentTarget); // Set all of the items to not have the selected class $('#items li a').removeClass('selected'); selectedItem.addClass('selected'); // Store a reference to what item was selected this.selectedItemId = selectedItem.data('id')); return false; } });
如今咱們能夠很容易的肯定哪些項被選中了,而且咱們無需遍歷DOM模型。此模式對於存儲外部數據來講很是有用,請記住,你能夠建立不需有端點相關聯的且存儲外部視圖數據的模型和集合。
此模式的缺點是若是你存儲了外部數據到你的模型或集合,它們不能真正遵循RESTful架構,由於它們不能完美的映射Web資源;另外,此模式可能會引發對象的膨脹;若是端點是嚴格接受JSON格式,那麼它可能會引發一些煩惱。
你或許會問本身,「我如何肯定我是否應該把外部數據放進視圖或者放進模型?」。若是你爲呈現添加了額外的屬性,例如容器的高度,那麼咱們把它添加到視圖。若是此屬性跟下層的數據模型有必定的關係,而後你想把它放進模型。總之一句話,根據實際的需求進行判斷。