JavaScript MVC框架backbone.js初探
什么是backbone
backbone不是脊椎骨,而是幫助開發(fā)重量級的javascript應(yīng)用的框架。
主要提供了3個(gè)東西:1、models(模型) 2、collections(集合) 3、views(視圖)
backbone.js文件本身很小,壓縮后只有5.3KB,作為一個(gè)框架級別的核心JS文件,這個(gè)數(shù)字很可怕。
除此之外,這個(gè)JS還必須依賴于另一個(gè)JS文件:underscore.js(包含許多工具方法,集合操作,js模板等等)。
簡介
用Backbone.Model表示應(yīng)用中所有數(shù)據(jù),models中的數(shù)據(jù)可以創(chuàng)建、校驗(yàn)、銷毀和保存到服務(wù)端。
當(dāng)models中值被改變時(shí)自動觸發(fā)一個(gè)"change"事件、所有用于展示models數(shù)據(jù)的views都會偵聽到這個(gè)事件,然后進(jìn)行重新渲染。
Backbone.Collection和我們平時(shí)接觸的JAVA集合類相似,具有增加元素,刪除元素,獲取長度,排序,比較等一系列工具方法,說白了就是一個(gè)保存models的集合類。
Backbone.View中可以綁定dom el和客戶端事件。頁面中的html就是通過views的render方法渲染出來的,當(dāng)新建一個(gè)view的時(shí)候通過要傳進(jìn)一個(gè)model作為數(shù)據(jù),例如:
Js代碼:
- var view = new EmployeeView({model:employee});
 
也就是說model就是以這種方式和view進(jìn)行關(guān)聯(lián)的。
特點(diǎn)
創(chuàng)建models或者views的語法:extends,相當(dāng)于類繼承
models的創(chuàng)建,銷毀,校驗(yàn)等一系列改變都會觸發(fā)相應(yīng)的事件
示例
需求:用backbone.js和jquery實(shí)現(xiàn)一個(gè)可編輯的員工信息表格。
功能:1、錄入員工信息。2、刪除員工信息。3、雙擊表格可對員工信息進(jìn)行修改。4、能對員工信息進(jìn)行有效性校驗(yàn)。5、能對員工信息進(jìn)行持久化。
設(shè)計(jì):
用Employee類(繼承自Backbone.Model)表示員工信息,包含ID、姓名、性別、年齡和職位字段。
Js代碼:
- window.Employee = Backbone.Model.extend({
 - // 模型值校驗(yàn)
 - validate:function(attrs){
 - for(var key in attrs){
 - if(attrs[key] == ''){
 - return key + "不能為空";
 - }
 - if(key == 'age' && isNaN(attrs.age)){
 - return "年齡必須是數(shù)字";
 - }
 - }
 - }
 - });
 
聲明Employee類之后就可以新增一個(gè)Employee的示例對象了:
Js代碼:
- var employee = new Employee();
 
Employee類中不必聲明ID、姓名等業(yè)務(wù)字段。當(dāng)需要給employee設(shè)置這些信息時(shí)候,只需要調(diào)用
Js代碼:
- employee.set({'id':1,'name':'Jason'});
 
當(dāng)然,如果需要對employee的信息進(jìn)行校驗(yàn),需要給Employee類配置一個(gè)validate方法,這個(gè)方法的參數(shù)attrs就是set進(jìn)去的json數(shù)據(jù)。這樣,當(dāng)employee里面的數(shù)據(jù)每次發(fā)生改變的時(shí)候都會先調(diào)用這個(gè)validate方法。
Model類定義好之后就可以開始定義集合類了,在集合類里面可以對里面的每個(gè)Model進(jìn)行增加,刪除等一系列操作,還可以調(diào)用fetch方法從server端獲取集合的初始值。
Js代碼:
- window.EmployeeList = Backbone.Collection.extend({
 - model : Employee,
 - // 持久化到本地?cái)?shù)據(jù)庫
 - localStorage: new Store("employees"),
 - });
 - window.Employees = new EmployeeList();
 
設(shè)置 localStorage屬性后Employees里面的數(shù)據(jù)自動會同步保存到本地?cái)?shù)據(jù)庫里面,每當(dāng)調(diào)用Employees.fetch()后又會從localStorage里面恢復(fù)數(shù)據(jù)。
View類主要負(fù)責(zé)一切和界面相關(guān)的工作,比如綁定html模板,綁定界面元素的事件,初始的渲染,模型值改變后的重新渲染和界面元素的銷毀等:
Js代碼:
- window.EmployeeView = Backbone.View.extend({
 - tagName : 'tr',
 - template : _.template($('#item-template').html()),
 - events : {
 - "dblclick td" : "edit",
 - "blur input,select" : "close",
 - "click .del" : "clear",
 - },
 - initialize : function(){
 - // 每次更新模型后重新渲染
 - this.model.bind('change', this.render, this);
 - // 每次刪除模型之后自動移除UI
 - this.model.bind('destroy', this.remove, this);
 - },
 - setText : function(){
 - var model = this.model;
 - this.input = $(this.el).find('input,select');
 - this.input.each(function(){
 - var input = $(this);
 - input.val(model.get(input.attr("name")));
 - });
 - },
 - close: function(e) {
 - var input = $(e.currentTarget);
 - var obj = {};
 - obj[input.attr('name')] = input.val();
 - this.model.save(obj);
 - $(e.currentTarget).parent().parent().removeClass("editing");
 - },
 - edit : function(e){
 - // 給td加上editing樣式
 - $(e.currentTarget).addClass('editing').find('input,select').focus();
 - },
 - render: function() {
 - $(this.el).html(this.template(this.model.toJSON()));
 - // 把每個(gè)單元格的值賦予隱藏的輸入框
 - this.setText();
 - return this;
 - },
 - remove: function() {
 - $(this.el).remove();
 - },
 - clear: function() {
 - this.model.destroy();
 - }
 - });
 
這個(gè)類里面的代碼比較多,但主要和界面的渲染有關(guān)。一個(gè)EmployeeView對象對應(yīng)table里面的一個(gè)tr元素。每次new一個(gè)EmployeeView對象的時(shí)候都會先調(diào)用initialize方法,這個(gè)方法里面綁定的事件確保了tr元素對應(yīng)的model值每次發(fā)生改變或者被刪除時(shí)都會同步到界面。也就是說當(dāng)每次操作界面對數(shù)據(jù)進(jìn)行修改后都是先把當(dāng)前的變更保存到view綁定的model對象里面,然后model里面的事件機(jī)制會自動觸發(fā)一個(gè)"change"事件對界面進(jìn)行修改。
template中使用的方法_.template($('#item-template').html())是前面提到的underscore.js中提供一個(gè)工具方法,可以通過界面的HTML模板和一個(gè)JSON生成動態(tài)的HTML,說白了就是把JSON里面的值填充到HTML模板中對應(yīng)的占位符里面去,牛X的是HTML模板里面支持一些常用的邏輯表達(dá)式如if,else,foreach等:
Html代碼:
- <script type="text/template" id="item-template">
 - <td><%= eid %></td>
 - <td class="username">
 - <div class="display"><%= username %></div>
 - <div class="edit"><input class="username" name="username"></input></div>
 - </td>
 - <td class="sex">
 - <div class="display"><%= sex=="1" ? "女":"男" %></div>
 - <div class="edit">
 - <select name="sex" class="sex" style="width:45px">
 - <option value="0">男</option><option value="1">女</option>
 - </select>
 - </div>
 - </td>
 - <td class="age">
 - <div class="display"><%= age %></div>
 - <div class="edit">
 - <input class="age" name="age"></input>
 - </div>
 - </td>
 - <td class="position">
 - <div class="display"><%= position %></div>
 - <div class="edit">
 - <input class="position" name="position"></input>
 - </div>
 - </td>
 - <td>
 - <a href="#" class="del">刪除</a>
 - </td>
 - </script>
 
setText方法主要負(fù)責(zé)把model里面的數(shù)據(jù)設(shè)置到每個(gè)tr里面的隱藏輸入域里面。
close方法被綁定到了input和select元素的blur事件中。當(dāng)用戶對單元格數(shù)據(jù)進(jìn)行修改后都會把鼠標(biāo)點(diǎn)擊到界面其他地方然后輸入框會自動隱藏并且把修改的數(shù)據(jù)顯示在表格上面。close方法首先從當(dāng)前被編輯的元素中拿到最新值,然后封裝成一個(gè)對象,調(diào)用model的save方法后首先執(zhí)行model的validate方法,如果校驗(yàn)通過則保存到本地存儲并觸發(fā)"change"事件。
最后還需要一個(gè)主界面View,這個(gè)View主要綁定了界面中的錄入表單的“增加”按鈕事件,Employees的相關(guān)事件以及頁面初始化時(shí)從本地存儲中恢復(fù)數(shù)據(jù):
Js代碼:
- window.AppView = Backbone.View.extend({
 - el : $("#app"),
 - events : {
 - "click .#add-btn" : "createOnEnter"
 - },
 - // 綁定collection的相關(guān)事件
 - initialize: function() {
 - Employees.bind('add', this.addOne, this);
 - // 調(diào)用fetch的時(shí)候觸發(fā)reset
 - Employees.bind('reset', this.addAll, this);
 - Employees.fetch();
 - },
 - createOnEnter : function(e) {
 - var employee = new Employee();
 - var attr = {};
 - $('#emp-form input,#emp-form select').each(function(){
 - var input = $(this);
 - attr[input.attr('name')] = input.val();
 - });
 - employee.bind('error',function(model,error){
 - alert(error);
 - });
 - // set方法中會自動調(diào)用model的validate方法進(jìn)行校驗(yàn),如果不通過則返回false
 - if(employee.set(attr)){
 - Employees.create(employee);
 - }
 - },
 - addOne : function(employee){
 - employee.set({"eid":employee.get("eid")||Employees.length});
 - employee.bind('error',function(model,error){
 - alert(error);
 - });
 - var view = new EmployeeView({model:employee});
 - $(".emp-table tbody").append(view.render().el);
 - },
 - addAll : function(){
 - Employees.each(this.addOne);
 - }
 - });
 
initialize方法中綁定了Employees的add和reset事件,也就是說每當(dāng)往Employees中添加一個(gè)model的時(shí)候都會調(diào)用AppView的addOne方法,這個(gè)方法主要綁定了model的error事件以及把EmployeeView生成的html插入到界面中的合適位置。
OK,萬事俱備,只欠啟動,整個(gè)應(yīng)用的初始化方法就是AppView的initialize方法,因此只需要新建一個(gè)AppView就可以了:
Js代碼
- window.App = new AppView();
 
整個(gè)示例的JS代碼很少,加上注釋只有100行左右,感興趣的可以下載看看。由于示例使用到了本地存儲,所以不要用IE運(yùn)行示例,你懂的。
原文:http://www.operamasks.org/blog/?p=211
【編輯推薦】















 
 
 











 
 
 
 