Backbone.js是什么?
Bakcbone.js 是一个JavaScript MVC框架,提供了良好的代码组织能力,可以方便地将应用程序解耦成可以复用的部分,为建立大型的单页面应用提供框架支持,目前的版本是 0.9.10(注:现在已到1.2.1版本)。通过将应用程序分解成MVC模式中不同职责的模块,带来了以下几点好处。
1、降低维护成本。数据、控制器、视图的更新都是独立进行的,互相之间都是松散耦合的。
2、解耦数据和视图,便于直接对业务逻辑进行单元测试。
3、便于团队合作,负责UI开发和业务逻辑开发的工程师可以分工并行工作。对于Web前端开发来讲,负责HTML模板和CSS的界面工程师和负责业务逻辑的JavaScript工程师可以协同工作。
Backbone.js算是比较轻量的MVC框架,所谓轻量,是说它只关注一个框架应该关注的最基本的事情——如何给应用分层、如何组织各种功能的代码。至于在实际开发中需要用到的Utils或UI组件,Backbone.js则没有提供任何支持。但Backbone.js所依赖的 Underscore.js是一个功能比较全面的非侵入式工具函数类库,算是在Utils方面的一个补充。
轻量并不意味着功能薄弱。首先,Backbone.js的精髓是它定义前端MVC的方式和编码哲学,并依据这些规定了如何去给代码分层,因此 Backbone.js能够让前端工程在可维护性和扩展性上都得到质的提升;同时,由于其良好且易于理解的结构,各个模块之间都是松散耦合的,虽然目前官方并没有提供根据实际需求build文件的功能,但如果你愿意,完全可以自己手工删掉源码中的Bakcbone.View只使用Model和 Collection;最后,Backbone.js的任何一个部分都是非常容易扩展的。因此,Backbone.js的功能实际上非常强大的。下面将介绍Backbone.js的主要组件(架构如图1所示)。
图1 架构图
Backbone.Events和Backbone.Sync
Backbone.Events和Backbone.Sync两个组件是Backbone.js异步通信、事件驱动的编程模型的基础。
Backbone.js中所有的组件都通过_.extend的方法“继承”了Backbone.Events所提供的功能,可以维护一套自己的事件订阅和回调列表。通过Events.on(event,[callback],[context])和Events.off([event], [callback], [context])两个方法来实现对事件的订阅和取消订阅。
早期版本中的事件订阅和取消订阅是通过Events.bind和Events.unbind两个方法实现的,目前的版本中还保留了这两个别名方法,但不推荐使用。
Backbone.js的所有组件都有一些内置的事件,可以查阅官方文档。除了预置事件外,通过Events.trigger(event,[*args])方法也可以方便地触发自定义事件。
从0.9版开始,Backbone.Events提供了Events.listenTo(other,event,callback)和 Events.stopListening([other],[event],[callback])两个新方法来通过另外一种形式实现事件的订阅和取消订阅。
与on和of f不同,这种方式将监听的主动权转换了。举个例子来说:有对象A和对象 B,B.on('someThingHappened',A.doSomeThing)是当对象BsomeThingHappened时候知对象A去 doSomeThing;而A.listenTo(B,'someThingHappend',A.doSomeThing)是对象A主动去盯着对象B,当它someThingHappend的时候去doSomeThing。
第二种方式最大的意义是变被动为主动,从而实现了IoC(Inversionofcontrol,控制反转)。监听者和被监听者之间没有了耦合,只要被监听的对象能够抛出指定的事件,就可以和监听者组合在一起,甚至不需要去关心被监听对象的类型,这对代码的复用和行为抽象有很大的帮助。在测试层面,可以轻易地把被监听对象换成mock的测试代码来模拟真实情况。
Backbone.Sync则将同服务器的通信封装了起来,当Collection和Model需要和服务器通信交换数据时,会去调用 Backbone.Sync中对应的方法并发送请求,如果服务器端支持RESTfulAPI就可以将整个通信过程描述得非常优雅并易于扩
Sync的实现可以是jQuery.ajax的封装,也可以是其他的类库提供的异步通信工具的封装。
Backbone.Model和Backbone.Collection
Backbone.js中的Model和Collection共同构成了MVC中的M层。Model的本质就是一组以keyvalue形式保存的数据,可以通过Backbone.Model.extend来定义自己的Model。
上面的示例代码中defaults属性定义了一组默认值,当Model初始化时,如果没有指定defualts中所定义的属性的值,就会用默认值来填充 Model;initialize方法会在Model被实例化时调用,用来进行一些初始化的操作;validate方法会在Model的save或set 方法被调用时执行,可以根据具体需求进行扩展。
通过Model的getters和setters可以实现对Model中属性的读写,并且当set方法被调用时,Model会将属性变化的事件广播给所有订阅者(通常是视图),驱动视图重新渲染或其他关联的Model的数据更新。
Collection是一组Model的集合,通过Collection可以将一组数据结构相同的Model有序地组织在一起,进行批量操作和管理等。同时,Collection代理了Undersore.js中众多用来操作Collection的工具方法,例如find、filter、map等。
Model和Collection都可以通过RESTfulAPI同服务器进行数据交换。
Backbone.View
Backbone.View是基于Backbone.js开发的Web App中的核心部分,负责用户交互事件的捕捉和处理、把用户输入导向Model或Collection、渲染视图、操作DOM等。Backbone.js 的内部实现依赖$变量,因此DOM操作的库可以在jQuery、Zepto或Ender等中选择。从目前的情况来看,在桌面浏览器中,Sizzle.js(jQuer y所使用的SelectorEngine)的性能还是甩开Zepto几条街的,因此面向桌面浏览器的开发还是推荐使用jQuery,移动端考虑到文件体积等因素推荐使用Zepto。
对于一个Backbone.View来讲,最重要的就是$el属性,$el是一个jQuery对象(取决于所采用的DOM操作类库),是一个视图的最外层容器。容器所采用的HTML标签可以通过tagName属性来指定,可以是ul也可以是header或其他任何标签,默认情况下是div。
容器内部的DOM渲染可以通过模板引擎来完成。Underscore.js本身提供了一个_.template的方法,因此Backbone.js不需要额外的模板引擎支持。当然,如果有特殊的需求,例如和后端共用模板文件,也可以选用Mustache等其他的模板引擎。
这样一个View就被渲染到界面上了。上面的代码中还监听了Model的change事件,当数据发生变化时,驱动视图重新渲染。当Model的数据比较丰富时,只有一个属性变化就重新渲染整个视图显然会带来性能上的隐患,因此这里的最佳实践就是把render的过程break-down成粒度更细的片段。
值得注意的是,当一个View不再被需要时,一定要记得销毁,除了销毁DOM对象外,也要销毁所有的事件监听器。在只有Events.on和 Events.off的年代,由于销毁View时需要逐一取消订阅所有的消息,经常由于忘记解除某个绑定导致产生被称为GhostView的垃圾对象,既无法被释放也无法被回收。这也是Events.listentTo方法带来的另外一个好处——只要调用Events.stopListening方法即可将此对象的所有事件监听器销毁。
所有的DOM事件也是通过$el来代理的,在Backbone.View中可以通过以下方法来方便地管理DOM事件。
Backbone.js的内部实现里,在View的构造方法中调用View. initialize后将继续调用View.delegateEvents方法,这个方法将解析events属性所定义的事件和回调列表,并将全部事件代理到$el对象上。由于使用的是事件代理,某些不支持冒泡的DOM事件则必须另外监听,如滚动条事件。
Backbone.Router和Backbone.History
Router是用来在URLHash和特定的动作或视图之间做映射的。
最后一句History.start是告诉Backbone.js开始对URLHash的变化进行监视,也可以随时通过History.stop来停止监视。同时,如果目标平台是支持HTML5Histor yAPI的,那么在s