初探Backbone
Backbone簡介
中文API:http://www.css88.com/doc/backbone/
英文API:http://backbonejs.org/
Backbone是構建javascript應用程序的一個優(yōu)秀的類庫。他簡潔、輕量級、功能實在。
backbone采用MVC模式,本身提供了模型、控制器和視圖從而我們應用程序的骨架便形成。
backbone依賴于underscore,他是一個類庫,提供了60多個函數(shù)處理數(shù)組操作、函數(shù)綁定,以及javascript模板機制。
于是我們來個圖看看幾個好基友吧:
有了初步印象后,我們來一步步看看他都干了些神馬。
模型
模型是保存應用程序數(shù)據(jù)的地方。我們可以把模型看做對應用程序原始數(shù)據(jù)的精心抽象,并且添加了一些工具函數(shù)和事件。
我們可以使用Backbone.Model的extend方法來創(chuàng)建Backbone模型:
var User = Backbone.Model.extend({initialize: function () { } });extend的第一個參數(shù)是一個對象,他成為了模型實例的屬性;
第二個參數(shù)是可選的類屬性的哈希,通過多次調用extend可以生成模型的子類,他們將繼承父親所有類和實例屬性:
var User = Backbone.Model.extend({//實例屬性instanceProperty: 'foo' }, {//類屬性classProperty: 'bar' }); assertEqual(User.instanceProperty, 'foo'); assertEqual(User.classProperty, 'bar');當模型實例化時,他的initialize方法可以接受任意實例參數(shù),其工作原理是backbone模型本身就是構造函數(shù),所以可以使用new生成實例:
var User = Backbone.Model.extend({initialize: function (name) {this.set({name: name});} }); var user = new User('刀狂劍癡'); assertEqual(user.get('name'), '刀狂劍癡');Ps:?assertEqual判斷相等
模型和屬性
使用set和get方法設置獲取實例的屬性:
var user = new User(); user.set({ name: '葉小釵' }); user.get('name'); //葉小釵 user.attributes;//{name: '葉小釵'}我們看到其實user.arributes是一個對象字面量,我們不會直接操作他,因為我們使用get/set方法可以進行我們的驗證流程。
我們使用validate方法來校驗一個實例屬性,默認情況沒有任何驗證,若是我們需要驗證的話:
var User = Backbone.Model.extend({validate: function (attr) {if (!attr.name || attr.name.length < 3) {return '名稱長度過短';}} });如果屬性合法,validate不會理睬之,不合法可以返回錯誤字符串或者Error對象,校驗失敗get/set方法就會觸發(fā)error事件:
var user = new User(); user.bind('error', function (model, error) {//錯誤處理 }); user.set({ name: '0' }); //給特定集合添加一個錯誤處理程序 user.set({ name: '0' }, { error: function (model, error) { } });使用hash名為default的對象來指定默認屬性,在創(chuàng)建一個實例模型時,任何沒有指定值的屬性都會被設置為默認值:
var Chat = Backbone.Model.extend({defaults: { name: '葉小釵'} }); assertEqual((new Chat).get('name'), '葉小釵');集合
在backbone中,模型實例的數(shù)據(jù)存放在多個集合中,為什么模型之間要使用獨立的集合,其原因有點復雜,但在實際中我們經常這么做(雖然我還沒做過)。
例如創(chuàng)建Twitter需要兩個集合followers和followees,兩者都有User的實例填充數(shù)據(jù),雖然兩個集合都是來自同一模型,但是各自包含了不同的實例數(shù)組,所以獨立形成集合。
針對模型,可以通過擴展backbone.collection來創(chuàng)建一個集合:
var Users = Backbone.Collection.extend({model: User });在上面的例子中,我們覆蓋了model屬性來指定與集合相關聯(lián)的模型(這里是User模型),雖然這個步驟不是必須的,但是為該集合設置一個默認的模型指向往往能派上大用場。
通常集合會包含單個模型的一個實例,而不是不同模型的多個實例。
在創(chuàng)建一個集合時,可以傳遞一個模型數(shù)組,比如backbone模型,如果定義了一個初始化實例函數(shù),在初始化時就會調用之:
var users = new Users([{ name: '葉小釵' }, { name: '素還真'}]);另一種方法是使用add方法為集合添加模型:
users.add({ name: '葉小釵' }); users.add([{ name: '葉小釵' }, { name: '素還真' }]);在為集合添加模型時會觸發(fā)add事件:
users.bind('add', function (user) {//... }); //移除一個模型 users.bind('remove', function (user) {//... }); //根據(jù)模型id獲取模型 var user = users.get('moduleId'); //集合中模型被修改后出發(fā)change事件 var user = new User({ name: '葉小釵' }); var users = new Backbone.Collection(); users.bind('change', function (rec) { //改變一個記錄 }); users.add(user); user.set({ name; '素還真'});控制集合內部順序
一個集合內部元素順序可以通過comparator方法控制,該方法的返回值便是你希望集合內部排序的規(guī)則:
var Users = Backbone.Collection.extend({comparator: function (user) {return user.get('name');} });返回值可以是值或者數(shù)字,具體例子我們下次有機會來試試看。
視圖
backbone的視圖并不是模板,而是一些控制類,他們處理模型的表現(xiàn)。
在很多MVC中視圖一般指html或者模板,他們在控制器中處理事件和渲染,但backbone中視圖:
視圖代表一個UI邏輯塊,負責一個簡單的DOM內容 var UserView = Backbone.View.extend({initialize: function () { },render: function () { } });不管視圖有沒有被插入頁面,每個視圖都知道當前的Dom元素,即this.el,el是從視圖的tagName、className或者id等屬性中創(chuàng)建的元素,沒有這些值el就是空div:
var UserView = Backbone.View.extend({tagName: 'span',className: 'users' }); var userView = new UserView();//<span class="users"></span>若是希望視圖綁定到頁面上已存在的元素上,直接指定el就好(必須在頁面加載后才能指定哦,不然找不到):
var UserView = Backbone.View.extend({el: $('.usets') }); //也可以實例化一個視圖時傳遞el(tagName、className\id) new UserView({ id: 'id' });渲染視圖
每個視圖都有一個render方法,默認情況下沒有任何操作,一旦視圖需要重繪便會調render方法,不同的視圖用不同功能的函數(shù)來覆蓋函數(shù),以處理模板渲染,并使用新的html來更新el:
var TodoView = Backbone.View.extend({template: _.template($('#tmpt').html()),render: function () {$(this.el).html(this.template(this.model.toJSON()));return this;} });backbone本身并不知道我們是怎么渲染視圖的,我們可以自己生產元素也可以使用模板類庫(一般用這個)。
在前面的代碼中,我們使用了this.model的本地屬性,他指向一個模型實例,在實例化時傳遞到視圖中,模型的toJSON方法實際上返回模型未加工時的原始屬性,可以在模板中使用:
new TodoView({ model: new Todo });委托事件
通過委托,backbone的視圖提供了一種添加事件到el的簡單快捷的方法:
var TodoView = Backbone.View.extend({events: {'change input[type=checkbox]': 'toggleDone','click .destroy': 'clear'},toggoleDone: function () { },clear; function () {} });events對象為{'eventType selector': 'callback'}這種格式,selector是可選的,不提供便綁定值el上。
委托利用了事件冒泡機制,意思是可以一直觸發(fā)而不管el內容是否改變(估計類似于delegate吧)。
上面的callback事件觸發(fā)時,他的上下文是視圖當前上下午,所以this中的this.model/el都可以使用。
綁定和上下文
事實上,每當視圖的模型發(fā)生變化時,就會觸發(fā)change事件,然后調用該函數(shù),這就意味這應用程序的視圖及HTML和與之對應的模型數(shù)據(jù)是同步的。
var TodoView = Backbone.View.extend({initialize: function () {_.bindAll(this, 'render', 'close');this.model.bind('change', this.render);},close: function () { } });需要注意在回調函數(shù)中的上下文已經改變,Underscore提供一個函數(shù):
_.bindAll(context, func);他將函數(shù)名字和一個上下文綁定,bindAll保證了所有給定的函數(shù)總是在指定的上下文中被調用,因為函數(shù)上下文總在胡亂變化,這樣做很有用。
模型銷毀需要視圖綁定delete事件,觸發(fā)時刪除el即可:
var TodoView = Backbone.View.extend({initialize: function () {_.bindAll(this, 'render', 'close');this.model.bind('change', this.render);this.model.bind('delete', this.remove);},remove: function () { $(this.el).remove() } });控制器
backbone的控制器將應用程序的狀態(tài)和url的hash片段關聯(lián)在一起,使url地址可分享,也可以作為書簽使用。
本質上,控制器是一些路由和函數(shù)組成,當導航到這些路由時那些函數(shù)便調用:
routes : {'help': 'help', //#help'search/:query': 'search', //#search/kiwis'search/:query/p:page': 'search', //#search/kiwis/p7'file/*peth': 'file' //#file/any/path.txt }參數(shù)以“:”開始,然后是參數(shù)名,當路由被調用時,所有參數(shù)都會傳遞到他的函數(shù),*為通配符,和參數(shù)一將和匹配的值一起傳遞到路由的函數(shù)中。
路由是以哈希對象中定義順序倒敘進行解析的,來創(chuàng)建一個控制器吧:
var PageController = Backbone.Controller.extend({routes: {'help': 'help', //#help'search/:query': 'search', //#search/kiwis'search/:query/p:page': 'search', //#search/kiwis/p7'file/*peth': 'file' //#file/any/path.txt },index: function () { },help: function () { },search: function () { } });當用戶被導航到“http://....com#search/param”時,不管手輸還是后退都會調用search函數(shù),并傳遞其參數(shù)param
若是希望ajax與seo更加友好,路由前綴就必須是“!/”,同時服務器端還需要做一點調整。
服務器同步
默認情況下,模型發(fā)生變化(保存),backbone就會使用ajax與服務器通訊(Backbone.sync),成功便更新客戶端模型。
要使用這個屬性便需要定義url,并且在服務器端處理rest形式請求,backbone會處理余下任務:
var User = Backbone.Model.extend({url: '/users' });url既可以是一個字符串也可以是一個函數(shù)返回字符串,backbone增刪查改幾個函數(shù)對應映射:
create => post /collection read => get /collection[/id] update => put /collection/id delete => delete /collection/id例如,我們要創(chuàng)建一個User實例,backbone會發(fā)送一個post請求道/uesrs,更新一個user實例,會發(fā)送至/users/id節(jié)點,服務器響應時會返回一個json格式的對象
若是要使用save(attr, option)函數(shù)將模型保存至服務器,可以隨意傳遞一個由屬性和請求項組成的hash對象,若是模型有id,假設該模型在服務器上以存在,存在就是put請求,不然就post請求添加數(shù)據(jù):
var user = new User(); user.set({ name: '葉小釵' }); user.save(null, {success: function () {//保存成功 } });所有save都是異步的,通過success或者failure來監(jiān)聽ajax回調,我們一般與jquery合用了。
填充集合
我們已經知道了如何創(chuàng)建/更新模型,但是第一次我們如何獲取模型數(shù)據(jù)呢?
這就是backbone集合出現(xiàn)的原因,他們用來請求遠程數(shù)據(jù)并保存至本地,和模型類似,必須給集合指定一個url來設置其數(shù)據(jù)源,若是沒有則默認使用與之關聯(lián)的模型url:
var Followers = Backbone.Collection.extend({model: User,url: '/user' }); //fetch用于刷新模型,該函數(shù)會請求數(shù)據(jù),若是遠程數(shù)據(jù)和當前模型不一致,將觸發(fā)change事件 Followers.fetch();集合的fetch函數(shù)將發(fā)送一個get請求道服務器,獲取遠程數(shù)據(jù),最后刷新集合觸發(fā)refresh事件。
可以使用refresh函數(shù)手動刷新集合,傳入一個模型對象即可,在第一次設置頁面時候用這個方法很方便,這里和頁面加載后發(fā)送get請求不一樣,我們可以傳遞json對象給refresh,而預先填充到集合中,比如:
Users.refresh({name; ''});自定義行為
在backbone視圖讀取或者保存模型到服務器時都會調用backbone.sync方法,我們可以覆蓋該方法來改變其默認行為(存入xml、本地存儲):
/* method 便是crud方法(create、read、update、delete) model 需要保存的模型 options 請求可選項,包括成功失敗函數(shù) */ Backbone.sync = function (method, model, options) {options.success(model); };每個模型或者集合只能覆蓋各自的sync函數(shù)。
我們來看一個擴展HTML5本地存儲的例子:
//所有待完成的項都保存至本地存儲命名空間“todos”中 Toto.prototype.localStorage = new Store('todos'); //重寫backbone.sync Backbone.sync = function (method, model, options) {var resp, store = model.localStorage || model.collection.localStorage;switch (method) {case 'read': resp = model.id ? store.find(model) : store.findAll(); break;case 'create': resp = store.create(model); break;case 'update': resp = store.update(model); break;case 'delete': resp = store.destroy(model); break;}if (resp) {options.success(resp);} else {options.error('not find');} }簡單實戰(zhàn)-官方版
首先,我們來看一個官方給出的例子:
具體代碼各位可以去下載,其中用到了很多類庫我們也暫時不去管它,我們現(xiàn)在就來試試能不能做。
這是一個簡單的to-do列表應用程序,我們希望能進行增刪查改操作,在頁面刷新后仍能保持數(shù)據(jù)。
頁面結構(核心)
1 <html lang="en"> 2 <head> 3 <meta charset="utf-8"> 4 <title>Backbone.js Todos</title> 5 <link rel="stylesheet" href="todos.css" /> 6 </head> 7 <body> 8 <div id="todoapp"> 9 <header> 10 <h1> 11 Todos</h1> 12 <input id="new-todo" type="text" placeholder="What needs to be done?"> 13 </header> 14 <section id="main"> 15 <input id="toggle-all" type="checkbox"> 16 <label for="toggle-all"> 17 Mark all as complete</label> 18 <ul id="todo-list"> 19 </ul> 20 </section> 21 </div> 22 <script src="js/json2.js" type="text/javascript"></script> 23 <script src="js/jquery.js" type="text/javascript"></script> 24 <script src="js/underscore.js" type="text/javascript"></script> 25 <script src="js/backbone.js" type="text/javascript"></script> 26 <script src="js/backbone.localStorage.js" type="text/javascript"></script> 27 <script src="js/todos.js" type="text/javascript"></script> 28 </body> 29 </html>頁面結構很簡單,其核心為:
一個文本框(#new-todo),用于創(chuàng)建新to-do
一個列表(#todo-list),用于展示
下面是基本的Todo模型,其擁有content與done屬性,同時提供toggle方法設置done屬性:
//todo是備忘錄的意思哦 var Toto = Backbone.Model.extend({defaults: {done: false},toggle: function () {//設置做了點擊就沒做,設置沒做點擊就做了...this.save({ done: !this.get('done') });} });然后我們定義一個todo的集合,也是保存todo模型的地方:
var TodoList = Backbone.Collection.extend({model: Todo,localStorage: new Store('todos'), //所有項目保存至todos命名空間//過濾已完成的項目done: function () {return this.filter(function (todo) {return todo.get('done');});},remaining: function () {return this.without.applay(this, this.done);} }); var Todos = new TodoList();因為這里用的是本地存儲所以引入了backbone.localstorage,下一步我們做用于顯示的視圖
1 var TodoView = Backbone.View.extend({ 2 tagName: 'li', //視圖時一個個li標簽 3 template: $('#item-template').template(), //獲取模板 4 events: { 5 'change .check': 'toggleDone', 6 'dblclick .todo-content': 'edit', 7 'click .todo-destroy': 'destroy', 8 'keypress .todo-input': 'updateOnEnter', 9 'blur .todo-input': 'close' 10 }, 11 initialize: function () { 12 //確保在正確作用域 13 _.bindAll(this, 'render', 'close', 'remove'); 14 //監(jiān)聽存儲模板修改以確定是否修改el 15 this.model.bind('change', this.render); 16 this.model.bind('destroy', this.render); 17 }, 18 render: function () { 19 //使用存儲模板更新el 20 var el = jQuery.tmpl(this.template, this.model.toJSON()); 21 $(this.el).html(el); 22 return this; 23 }, 24 tiggleDone: function () { 25 this.model.toggle(); 26 }, 27 edit: function () { 28 $(this.el).addClass('editing'); 29 this.input.focus(); 30 }, 31 close: function () {//關閉編劇狀態(tài) 32 this.model.save({ content: this.input.val() }); 33 $(this.el).removeClass('editing'); 34 }, 35 //按下回車鍵結束編輯 36 updateOnEnter: function (e) { 37 if (e.keyCode == 13) 38 e.target.blur(); 39 }, 40 remove: function () { 41 $(this.el).remove(); 42 }, 43 destroy: function () { 44 this.model.destroy(); 45 } 46 });我們將血多事件委托給管理更新,完成和刪除todo視圖,例如每當復選框變化toggleDone就會調用,并切換done屬性,最后又會觸發(fā)change事件,導致視圖重新渲染。
來看看我們的模板:
<script type="text/template" id="item-template"> <div class="view"><input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> /><label><%- title %></label><a class="destroy"></a> </div> <input class="edit" type="text" value="<%- title %>" /> </script> <script type="text/template" id="stats-template"> <% if (done) { %><a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a> <% } %> <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div> </script>PS:我這里寫了大概思想,有不一致的地方各位看看就行。
最后我們來一個終止:
1 var AppView = Backbone.View.extend({ 2 el: #('#todoapp'),//綁定至現(xiàn)有骨架 3 events: { 4 'keypress #new-todo': 'createOnEnter', 5 'click .todo-clear a': 'clearCompleted' 6 }, 7 /* 8 初始化時,將相關事件綁定給todos集合,當添加或者修改集合中元素時觸發(fā)事件, 9 通過載入可能存在本地存儲中的記錄來初始化數(shù)據(jù) 10 */ 11 initailize: function () { 12 _.bindAll(this, 'addOne', 'addAll', 'render'); 13 this.input = this.$('#new-todo'); 14 Todos.bind('add', this.addOne); 15 Todos.bind('refresh', this.addAll); 16 Todos.fetch(); 17 }, 18 addOne: function (todo) { 19 var view = new TodoView({model: todo}); 20 this.$('#todo-list').append(view.render().el); 21 }, 22 //...... 23 }); 24 25 var App = new AppView();當頁面首次加載后,Todos集合將填充數(shù)據(jù),然后觸發(fā)refresh事件,將調研addAll來獲取todo模型,生成todoview視圖,并將它們添加至#todo-list中。
當有新的todo模型添加至Todos時,會觸發(fā)add事件,調用addOne做到最后的視圖更新。
轉載自:http://www.cnblogs.com/yexiaochai/archive/2013/07/27/3219402.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結
以上是生活随笔為你收集整理的初探Backbone的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql的DbUtils_Dbutil
- 下一篇: mysql数据库支持emoji表情的详解