By:软件11 王思伦 2013-10-4
Backbone简述:
Backbone基于MVC架构,用于开发重量级Javascript应用的框架。
如上文所述,Backbone包含多种类,但主要包含了三种:Backbone.Model, Backbone.Collection, Backbone.View。
它能让你像写 java 代码一些写 js 代码,定义类,类的属性以及方法。更重要的是它能够优雅的把原本无逻辑的 javascript 代码进行组织,并且提供数据和逻辑相互分离的方法,减少代码开发过程中的数据和逻辑混乱。
另外backbone必须要依赖另一个文件:underscore.js(包含许多工具方法,集合操作,js模板等等),我在做backbone测试的时候就因为没有包含underscore.js搞得无法显示一头雾水。
Backbone主体介绍:
Backbone.Model表示应用中所有数据,model中的数据可以创建(new)、校验(validate)、销毁(destroy)和保存到服务端(localstorage)。当models中值被改变时自动触发一个"change"事件(Backbone.Event)、所有用于展示model数据的view都会侦听到这个事件,然后进行重新渲染(render)。
Backbone.Collection有点像JAVA集合类(list/vector),具有增加元素,删除元素,获取长度,排序,比较等一系列工具方法,简而言之是一个保存models的集合类。
Backbone.View中可以绑定dom element和客户端事件,一个view可以对应一个element。页面中的html就是通过view的render方法渲染出来的,当新建一个view的时候通过要传进一个model作为数据,例如:
-
var view = new EmployeeView({model:employee});
也就是说model就是以这种方式和view进行关联的。
Model
声明:
在创建Model或者View等backbone类的时候,我们总会使用如下语句:Backbone.Model.extend
其中使用了extends继承model和view,因此models的创建,销毁,校验等一系列改变都会触发相应的事件
实例化:
声明Employee类之后就可以新增一个Employee的实例对象了:
-
var employee = new Employee();
赋值:
Employee类中不必声明ID、姓名等业务字段。当需要给employee设置这些信息时候,只需要调用
-
employee.set({'id':1,'name':'Jason'});
校验:
当然,如果需要对employee的信息进行校验,需要给Employee类配置一个validate方法,这个方法的参数attrs就是set进去的json数据。这样,当employee里面的数据每次发生改变的时候都会先调用这个validate方法。
Collection
Model类定义好之后就可以开始定义集合类Collection了,在集合类里面可以对里面的每个Model进行增加,删除等一系列操作,还可以调用fetch方法从server端获取集合的初始值。
-
1 window.EmployeeList = Backbone.Collection.extend({ 2 model : Employee, 3 // 持久化到本地数据库 4 localStorage: new Store("employees"), 5 6 }); 7 window.Employees = new EmployeeList();
设置 localStorage属性后Employees里面的数据自动会同步保存到本地数据库里面,每当调用Employees.fetch()后又会从localStorage里面恢复数据。
View
View类主要负责一切和界面相关的工作,比如绑定html模板,绑定界面元素的事件,初始的渲染,模型值改变后的重新渲染和界面元素的销毁等:
-
1 window.EmployeeView = Backbone.View.extend({ 2 tagName : 'tr', 3 template : _.template($('#item-template').html()), 4 events : { 5 "dblclick td" : "edit", 6 "blur input,select" : "close", 7 "click .del" : "clear", 8 }, 9 initialize : function(){ 10 // 每次更新模型后重新渲染 11 this.model.bind('change', this.render, this); 12 // 每次删除模型之后自动移除UI 13 this.model.bind('destroy', this.remove, this); 14 }, 15 setText : function(){ 16 var model = this.model; 17 this.input = $(this.el).find('input,select'); 18 this.input.each(function(){ 19 var input = $(this); 20 input.val(model.get(input.attr("name"))); 21 }); 22 }, 23 close: function(e) { 24 var input = $(e.currentTarget); 25 var obj = {}; 26 obj[input.attr('name')] = input.val(); 27 this.model.save(obj); 28 $(e.currentTarget).parent().parent().removeClass("editing"); 29 }, 30 edit : function(e){ 31 // 给td加上editing样式 32 $(e.currentTarget).addClass('editing').find('input,select').focus(); 33 }, 34 render: function() { 35 $(this.el).html(this.template(this.model.toJSON())); 36 // 把每个单元格的值赋予隐藏的输入框 37 this.setText(); 38 return this; 39 }, 40 remove: function() { 41 $(this.el).remove(); 42 }, 43 clear: function() { 44 this.model.destroy(); 45 } 46 });
这个类里面的代码比较多,但主要和界面的渲染有关。一个EmployeeView对象对应table里面的一个tr元素。每次new一个EmployeeView对象的时候都会先调用initialize方法,这个方法里面绑定的事件确保了tr元素对应的model值每次发生改变或者被删除时都会同步到界面。也就是说当每次操作界面对数据进行修改后都是先把当前的变更保存到view绑定的model对象里面,然后model里面的事件机制会自动触发一个"change"事件对界面进行修改。
template中使用的方法_.template($('#item-template').html())是前面提到的underscore.js中提供一个工具方法,可以通过界面的HTML模板和一个JSON生成动态的HTML,说白了就是把JSON里面的值填充到HTML模板中对应的占位符里面去,HTML模板里面还支持一些常用的逻辑表达式如if, else, foreach等。
setText方法主要负责把model里面的数据设置到每个tr里面的隐藏输入域里面。
close方法被绑定到了input和select元素的blur事件中。当用户对单元格数据进行修改后都会把鼠标点击到界面其他地方然后输入框会自动隐藏并且把修改的数据显示在表格上面。close方法首先从当前被编辑的元素中拿到最新值,然后封装成一个对象,调用model的save方法后首先执行model的validate方法,如果校验通过则保存到本地存储并触发"change"事件。
Application:
最后还需要一个主界面View,这个View主要绑定了界面中的录入表单的“增加”按钮事件,Employees的相关事件以及页面初始化时从本地存储中恢复数据:
1 window.AppView = Backbone.View.extend({ 2 el : $("#app"), 3 events : { 4 "click .#add-btn" : "createOnEnter" 5 }, 6 // 绑定collection的相关事件 7 initialize: function() { 8 Employees.bind('add', this.addOne, this); 9 // 调用fetch的时候触发reset 10 Employees.bind('reset', this.addAll, this); 11 Employees.fetch(); 12 }, 13 createOnEnter : function(e) { 14 var employee = new Employee(); 15 var attr = {}; 16 $('#emp-form input,#emp-form select').each(function(){ 17 var input = $(this); 18 attr[input.attr('name')] = input.val(); 19 }); 20 employee.bind('error',function(model,error){ 21 alert(error); 22 }); 23 // set方法中会自动调用model的validate方法进行校验,如果不通过则返回false 24 if(employee.set(attr)){ 25 Employees.create(employee); 26 } 27 }, 28 addOne : function(employee){ 29 employee.set({"eid":employee.get("eid")||Employees.length}); 30 employee.bind('error',function(model,error){ 31 alert(error); 32 }); 33 var view = new EmployeeView({model:employee}); 34 $(".emp-table tbody").append(view.render().el); 35 }, 36 addAll : function(){ 37 Employees.each(this.addOne); 38 } 39 });
initialize方法中绑定了Employees的add和reset事件,也就是说每当往Employees中添加一个model的时候都会调用AppView的addOne方法,这个方法主要绑定了model的error事件以及把EmployeeView生成的html插入到界面中的合适位置。
万事俱备,整个应用的初始化方法就是AppView的initialize方法,因此只需要新建一个AppView就可以了:
window.App = new AppView();