初尝backbone
backbone的基础知识在此将不再进行介绍。自己后续应该会整理出来,不过今天先把这几天学的成果用一个demo进行展示。
后续可运行demo将会在sinaapp上分享,不过近期在整理sinaapp上demo分享版块的重构,恕不能及时更新上去。
手把手教你搭建Hello World
虽然这次的开篇程序叫做helloworld有点牵强,但是我还是喜欢叫它为hello world~^_^
以下程序改编自著名的todos程序,todos是什么?其实如果学过backbone的,肯定看到过todos的实例,网上对todos分析也不少。那我为什么要对ta进行改编呢?我觉得,不论学习什么新的东西,从简单的东西中能够看到本质,才是学习的入口点。其实我是打着改编的旗号,只是在不影响反映backbone本质的前提下,把代码写得简洁简洁再简洁一些,然后本质的东西浮出水面=。=叫做helloworld,~just joking^_^
对于外部引入的代码文件不再赘述,可以查看图片(你可以留意下路径,对于html中路径的书写有影响)
然后下面主要是对index.html代码和main.js文件进行解析,当然重头戏肯定是main.js
index.html
1 <!doctype html> 2 <html> 3 <head> 4 <title>index</title> 5 <style type="text/css"> 6 ul { 7 list-style-type: none; 8 } 9 #todo-list .done .todo-content { 10 text-decoration: line-through; 11 color: rgb(119, 119, 119); 12 } 13 .todo .todo-content { 14 display: inline; 15 } 16 </style> 17 <script src="./lib/json2.js"></script> 18 <script src="./lib/jquery.js"></script> 19 <script src="./lib/jquery.tmpl.js"></script> 20 <script src="./lib/underscore.js"></script> 21 <script src="./lib/backbone.js"></script> 22 <script src="./lib/backbone.localstorage.js"></script> 23 <script src="./js/main.js"></script> 24 </head> 25 <body> 26 <div id="todoapp"> 27 <div class="title"> 28 <h1>Todos</h1> 29 </div> 30 31 <div class="content"> 32 <div id="create-todo"> 33 <input id="new-todo" value="" placeholder="what needs to be done?" type="text"/> 34 </div> 35 36 <div id="todos"> 37 <ul id="todo-list"></ul> 38 </div> 39 </div> 40 </div> 41 42 <!-- template --> 43 <script type="text/template" id="item-template"> 44 <div class="todo {{if done}}done{{/if}}"> 45 <div> 46 <input class="check" type="checkbox" {{if done}}checked="checked"{{/if}} /> 47 <div class="todo-content">${content}</div> 48 </div> 49 </div> 50 </script> 51 <!-- template ---> 52 53 </body> 54 </html>
main.js
1 jQuery(function($) { 2 /* 3 *model部分 4 */ 5 window.Todo = Backbone.Model.extend({ 6 defaults: { 7 done: false 8 }, 9 10 toggle: function() { 11 this.save({done: !this.get("done")}); 12 } 13 }); 14 15 /* 16 *collection部分 17 */ 18 window.todoList = Backbone.Collection.extend({ 19 model: Todo, 20 21 localStorage: new Store("todos"), 22 23 done: function() { 24 return this.filter(function(todo){return todo.get("done");}); 25 }, 26 27 remaining: function() { 28 return this.without.apply(this,this.done); 29 } 30 31 }); 32 33 /* 34 *创建一个全局范围的集合实例 35 */ 36 window.Todos = new todoList; 37 38 /* 39 *绑定模型的change事件,并且当事件发生的时候渲染模板 40 */ 41 window.TodoView = Backbone.View.extend({ 42 tagName: "li", 43 44 template: $("#item-template").template(), 45 46 events: { 47 "change .check": "toggleDone" 48 }, 49 50 initialize: function() { 51 //确保在正确的作用域调用函数 52 _.bindAll(this,"render","close","remove"); 53 54 this.model.bind("change",this.render); 55 this.model.bind("destroy",this.remove); 56 }, 57 58 render: function() { 59 //console.log(this.model); 60 var element = jQuery.tmpl(this.template, this.model.toJSON()); 61 $(this.el).html(element); 62 63 return this; 64 }, 65 66 toggleDone: function() { 67 this.model.toggle(); 68 } 69 70 }); 71 72 /* 73 *负责顶层DOM 74 */ 75 window.AppView = Backbone.View.extend({ 76 77 el: $("#todoapp"), 78 79 events: { 80 "keypress #new-todo": "createOnEnter" 81 }, 82 83 initialize: function() { 84 _.bindAll(this,"addOne","addAll","render"); 85 86 this.input = this.$("#new-todo"); 87 88 Todos.bind("add",this.addOne); 89 Todos.bind("refresh",this.addAll); 90 Todos.bind('all',this.render); 91 92 Todos.fetch(); 93 }, 94 95 96 addOne: function(todo) { 97 var view = new TodoView({model: todo}); 98 this.$("#todo-list").append(view.render().el); 99 }, 100 101 addAll: function() { 102 Todos.each(this.addOne); 103 }, 104 105 createOnEnter: function(e) { 106 if(e.keyCode == 13) { 107 var value = this.input.val(); 108 109 if(!value) return; 110 111 Todos.create({content: value}); 112 113 this.input.val(''); 114 } 115 } 116 117 }); 118 119 window.App = new AppView; 120 121 });
代码解读:对于main.js我们可以理解为三部分:model、collection、view,当然不要理解为这是MVC的缩写来源,MVC中的C的缩写来自control,只是这里没有用到,控制器本质上路由和函数(在此不再展开,因为之后将会在介绍backbone细节的文章中展开)。~在main.js中,model、collection、view三者是如何协调工作的呢?
以上代码阅读理解起来并不难。~我可以用我自己的话来讲下整个应用。
collection,我喜欢把它叫做“一个模型的collection”。我觉得也可以这么理解"用通俗话讲就是,collection可以理解为一个容器,容器中可以放东西,这里的东西就是你定义的模型。一个collection对应一个模型"。model和collection共同实现了模型层。如果将前端的MVC和后端的MVC进行联系,我们可以这么以为。M层的作用是为了实现数据的持久化存储和更新(对于更新会涉及到服务器通信,这里不再展开),数据持久化存储,与数据库在后台的作用像类似。我们定义的Backbone.model.extend就是相当于一张表,我们在后续代码中就是创建这张表的一个个实例,然后把实例扔给collection。不得不提一下的是,单纯的collection和model是没有持久化功能,你可以自己采取持久化策略:webSocket
、XML传输流或本地存储(HTML5 localstorage)。在上述代码中,我们采用了backbone.localStorage.js,实现loccalstorage。你可能会问,为什么要对数据进行持久?其实,还有重要的一点没有指出来,MVC到底是怎么进行工作的?当model和collection都准备就绪的时候,我们来看一下view部分。
当我们在输入框中输入文本,单击回车,代码部分到底发生了什么呢?请看AppView中的createOnEnter函数,其中的Todos.create({content:value})就是相当于创建了一个类型为Todo模型的实例,并且把该实例添加到Todos集合中,这一步导致model的改变。model的改变触发了change事件,根据TodoView中函数的绑定,将会执行render函数,使用存储的模板来更新el。如果你通过console.log(this.template, this.model.toJSON())。你会发现,输出的内容是一条记录,也就是刚刚创建的Todo模型的新实例。就是相当于后台中,一条新的更新记录。而所有记录则是保存在collection的实例Todos中。在代码中加入
1 console.log("就是一条记录:" + this.template, this.model.toJSON()); 2 console.log("集合记录: " + Todos.length);