BackBone及其实例探究_动力节点Java学院整理
mvc简介
基本介绍
mvc即模型(model),视图(view)和控制(controller),旨在实现web系统的职能分工,具体来说就是使业务逻辑和数据显示分离。
在mvc中,视图(view)为用户提供交互,模型(model)负责处理数据和业务逻辑,控制器(controller)则是view与model之间沟通的桥梁。
mvc一个很重要的标志就是,视图(view)与模型(model)没有直接的交互,而是通过控制器(controller)来沟通。具体地:用户通过view进行输入,controller负责把输入传递给model,model处理,存取数据,然后controller将处理的结果返回view进行展示。
优缺点
mvc架构的优势是显著的,但也存在一些缺点。
一方面,mvc能极大地降低数据与视图之间的耦合性,具有较高的可维护性和复用性,还能提高分工效率和降低生命周期的成本,从而有利于软件工程化管理。
然而,mvc架构却不是那么容易理解,且会增加系统的复杂性,还会降低视图层访问数据层的效率,不适用于小型工程的开发。
backbone
backbone是一个重要的前端mvc框架,用于支持javactript应用的重量级开发。下面将对backbone.events(和控制器有着密切联系)以及backbone.model(和模型有着密切联系)的应用进行介绍,并结合实例分析backbone框架是如何支持前端mvc的架构的。所有的学习资料来自于backbone官网:
backbone.events
在backbones中,事件可以作为一个任何一个对象的模型,对象能够绑定和触发一个事件被命名的事件。
对于一个被声明了的对象object(下文的讨论,所有的对象都以object为例),首先为object添加extend。
._extend(object,backbone.events);
然后为对象添加一个事件:格式为:
object.on(event,callback,[context]);
第一个参数为事件名,第二参数为事件调用时的回调函数。
以下面的事件为例:”alert”为事件名,function(msg)为回调函数,msg为触发事件时所传入的参数,与触发器中的参数一致。
object.on(“alert”,function(msg){alert(msg);});
触发object.on进行回调的是object.trigger(“alert”, ”it's an event”)。
同object.on,object.trigger第一个参数为事件名,而第二个参数传入的是object.on中回调函数所需要的参数。事件通过事件名被绑定在一个对象上。可以看到这个事件在绑定前是不需要声明的,事件能够被触发的内在联系是共同的对象和事件名。特别地,如果事件名为”all”,那么调用任何object.trigger时,都会触发object.on中的回调函数。
与标准的事件绑定相对应的,标准触发的格式为:
object.trigger(event,[*arg]);
也可用js中类似属性的定义来绑定一个对象的多个事件,如:
object.on{ “setup”:function_a(), “change”:function_b(), “destroy”:function_c() }
触发相应的”setup”,”change”,和”destroy”时可以分别调用相应的function_a()等。
如果需要解除对象上一个事件的绑定,则使用object.off(event,callback,[context])函数。标准参数列表与相应的object.on一致。
具体的,以上面的”setup”:function_a()为例:
//解绑定 object.off(“setup”,function_a); //移除所有的”setup”事件(可能绑定多个回调函数) object.off(“setop”); //移除所有的绑定function_a这个回调函数的事件 object.off(function_a); //移除所有的事件 object.off()。
如果希望事件被触发一次就解绑定,则直接使用object.once(event,callback,[context])。
使用object.listento(other_object, event, callback),可监听其他对象的事件,事件被触发时同样调用callback的回调函数。类似的,可使用标准的stoplistento([other_object], [event],[callback]),进行解绑定。
listento在mvc架构中很有用,如使用视图类的对象监听model类的数据处理事件,并进行回调(一般是对数据进行展示)。
backbone.model
models是一个javascript应用的核心。很多时候,models需要处理许多数据,以及与这些数据相关的逻辑。然而,对于javascript来说,并没有类似java那样的类的结构的存在。然而在实际应用中,却需要这样的模型结构。而backbone.model为js提供了很好的拓展,让javascript也能实现类的结构和功能,更准确地说,实现类似c#中的属性。下面给出官方介绍的一个简化的实例(为了简洁起见,之后所有介绍的模型都采用model,而模型的对象都采用model):
//类似类的定义 var model = backbone.model.extend({ mydata:”year2013”, myfunction:function(){mydata = “year2014”;} ); //类似类的实例化 var model = new model; //类似对象调用成员方法 my_class.myfunction();
可以看到以上的代码就实现了类似类的结构。与传统javascript对象的属性区别在于,一个是具体的对象(只能使用一次),另一个是一个类的模型(可以被多次实例化)。
既然已经有了类似类的实例化功能,那么自然,backbone.model也为我们提供了类似构造函数的拓展。当实例化一个对象时,采用new model([attributes], [options])的标准格式进行实例化。例如:
new model({ mydata:”2013” });
此外,经过拓展backbone的js同c#一样,还具有属性的get和set函数。类似下面地进行调用:
var date = model.get(mydata); model.set(mydata,”2014”);
此外,backbone.model还为js拓展了许多模型的默认函数,不一一介绍。需要使用的时候,建议在官网http://backbonejs.org/#model-url的model进行查询。
backbone实例:todo
下面结合官网中所给出的实例todos来看一看backbone在前端mvc的具体应用。
这是一个纯js实现的任务单application。
代码中设计了四个“类”来实现任务单的功能:
//model层面,实现一条任务的数据操作 var todo = backbone.model.extend({}); //model层面,实现整个任务栏的数据操作,collection拓展了本地存储功能 var todolist = backbone.collection.extend({}); //backbone.view.extend(),view和controller层面 //负责接收用户操作和修改model的数据,在render()函数中展示数据 //与todo的dom元素相关 var todoview = backbone.view.extend({}); //最顶层的ui以及控制器 var appview = backbone.view.extend({});
首先来分析实例中的model,采用自下而上的分析方法:
todo代表每一条todo中的任务,与之相关的逻辑是其是否已经完成,需要指定其是否完成,需要初始属性done:false,另外需要设置done的函数,为此添加属性:
toggle:function(){ this.save({done:!this.get(“done”);}); }
可以看到这个属性在数据层从逻辑上实现了view层复选框的功能。
对于todolist,使用collection这一model,为的是实现的数据本地存储。
属性含有model:todo,即低层的,实现一个任务的实例。
此外,还需要localstorage : new backbone.localstorage(“todos-backbone”)。用来进行本地存储。
在数据处理方面,需要对低层的done属性进行封装,实现了done:function(),返回被改变的done的位置。
在view和controller层面,自下而上地依次设计了下面两个“类”。
var todoview = backbone.view.extend({}); var appview = backbone.view.extend({});
首先,对于todoview,设置了一系列事件的响应,todoview在controller层面,对于view,接收这样的事件监听:
events: { "click .toggle" : "toggledone", "dblclick .view" : "edit", "click a.destroy" : "clear", "keypress .edit" : "updateonenter", "blur .edit" : "close" }
以clear()为例,clear中调用this.model.destroy()进行数据层的处理,这就反馈到了数据层。
clear: function() { this.model.destroy(); }
另一方面,在初始化时,指定了todoview对数据层的this.model添加了监听,当this.model的change事件发生,todoview发生this.remove(render亦同),而这是表现层的内容,这样就反馈到了表现层。
initialize: function() { this.listento(this.model, 'change', this.render); this.listento(this.model, 'destroy', this.remove); }
这样一来便完成了从view(输入) -> controller -> model(处理) -> contoller -> view(展示)的mvc架构。从上面可以看到,从数据的接收,处理,返回,展示,所有过程都是自动的,且view和model没有任何的耦合。这是标准的mvc模式。
从todoview向上则是总体的appview。它的逻辑行为也是类似的,亦是在视图层接收数据,通过view类特有的结构反馈到数据层,然后数据层又无耦合地将数据返回到视图层进行展示。但是作为最顶层的view和controller,其在视图层的render函数中实现的是总体的渲染,更底层的渲染则交给todoview,在控制层的事件响应和事件监听实现的则也是封装度更高的处理。如添加一个todo,依次有如下的传递:
//view->controller "keypress #new-todo": "createonenter" //controller->model createonenter: function(e) { if (e.keycode != 13) return; if (!this.input.val()) return; todos.create({title: this.input.val()}); this.input.val(''); } //model -> controller this.listento(todos, 'add', this.addone); //controller -> view addone: function(todo) { var view = new todoview({model: todo}); this.$("#todo-list").append(view.render().el); }
在最后一个addone函数中,则又包含了底层的mvc转换流程,但是这显然不是我们所关心的,它是由底层封装好的,在backbone的框架会自动地进行mvc的逻辑传递。
todo小结
这就是整个todo totuorial的结构。总的说来,backbone所为我们提供的这种特定的结构很好地实现了mvc的架构,完成了view与model的分离。而controller则很好地充当了转换者的角色。
心得体会
由于之前没有学习过前端mvc的知识,因此在探究todo实例的时候遇到了很多困难。todo实例与传统的网页应用有很大的差异:
a) 无静态html代码,为纯js实现
b) 数据与界面的交互原理十分难懂
c) view的构建与数据的处理鸿沟太大。
d) 事件监听与事件绑定用途不同,响应与回调流程容易混淆
一开始,真的很难理解todo的结构,但是后来,反过来,从mvc的结构来思考,从视图开始分析,探究view->controller->model->controller->view的线索。便得出了整体的结构以及controller的传递过程。可以说,理解controller这个桥梁对于贯通整个mvc结构有至关重要的作用。建议从view的输入下手,对controller进行分析。
另一方面,可以适当采取自上而下与自下而上的结构进行分析。如对view的分析,采用自上而下方法较为容易入手,而models采用自下而上的方法能帮助我们更清晰地理解models的结构。
推荐阅读