本文紧接第一部分:《BackboneJS框架的技巧及模式(1)》
作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs
在实际项目中,需求是经常变化的,因此,终端返回的JSON数据格式也是如此。如果你的视图和下层的数据模型是紧耦合的,那么这是一种痛苦。有鉴于此,我为所有的对象都创建getter和setter函数。
此模式使用较广。如果任何底层的数据结构发生了变化,那么视图层无需随之更新;你会有一个数据访问点,所以你不太可能忘记做深度复制,编写的代码将会更易于维护和调试。此模式的缺点在于它可能会导致模型或集合有一点点膨胀。
下面我们看一个例子来阐明此模式。假想我们有一个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 }); } } });
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 }); } } });
顺便说一句,此模式既可以被认为是Facade装饰模式,因为它隐藏了创建对象复制的复杂性,也可以认为是Bridge桥接模式,因为它可以用于将数据转换为所期望的格式。
最佳实践是对任何对象都使用getters 和setters 函数。
尽管Backbone.js有模型和集合映射到RESTful端点的规则,有时候你可能会花大量时间查找你想要的存储在服务器端的数据或集合。
有一些关于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>
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; } });
此模式的缺点是如果你存储了外部数据到你的模型或集合,它们不能真正遵循RESTful架构,因为它们不能完美的映射Web资源;另外,此模式可能会引起对象的膨胀;如果端点是严格接受JSON格式,那么它可能会引起一些烦恼。
你或许会问自己,“我如何确定我是否应该把外部数据放进视图或者放进模型?”。如果你为呈现添加了额外的属性,例如容器的高度,那么我们把它添加到视图。如果此属性跟下层的数据模型有一定的关系,然后你想把它放进模型。总之一句话,根据实际的需求进行判断。