《Backbone 应用开发》读书笔记

假语村言

所有架构设计的目的都是让软件更好,但是如何定义这个“好”呢?
基本要求是满足功能性需求,进而考虑性能、可扩展性、可维护性等要求,上面这些在实际工作中会有人在不同阶段跳出来,讲一堆不满足会死的理由。这些可以说是架构设计第一层次的要求,如果对比马斯洛需求模型,这个只能算是温饱阶段。
对于苦逼又要面(儿)的码农来说,好的架构设计,或者说在此设计理念之上的框架、库,要足够时髦,说出来以后要足够酷,有个性。不夸张的说,大多数时候码农选框架要比女人选衣服费劲得多,要试穿多次,反复比较,一旦衣服不满意,码农也会发小姐脾气。虽说发小姐脾气好像不太体面,但是讲究衣服穿着的人,如果再有点品位,应该算是进入小康了吧。
再往上…如闲云野鹤般,偶尔指点一下江山?或者人家也没这么悠哉,而是站的高看的远,不断变着花样地解决着勤劳的码农早已熟视无睹的问题,或者走在前面,不经意间引领了你行进的方向。只能YY一下,这样的人,还没认识的。很多人被挡在外面进不了这一层,可能就是因为太勤奋了,或者说战术上过于勤奋,战略上又过于懒惰。
下笔千言,离题万里。是病,得改!

什么是MVC?(参见我的 另一篇文章)对于有点强迫症,非要弄清哪是M,哪是V或C的人,恐怕真的有点痛苦,亲身经历过,代码草草看过一遍,拿着MVC来套,来分解,整了半天说:你这不符合MVC,有问题。这个阶段,看山是山,看水是水,一旦不那么清楚,自己就先蒙了。Backbone应用开发告诉我们,很多Javascript MVC框架(包括Backbone)并没有严格遵循传统的MVC,各个部分的内涵不同,划分也有区别,但是仍遵循了数据和表现的分离,所以还是叫MV*吧。题外话,近几年流行的单页面应用(SPA)也是一种数据和表现的分离, WEB端专注于表现,服务器端专注于数据,二者通过RESTful API建立联系,这也促成了很多BaaS(Backend as a service)的出现。
言归正传,因为是笔记,后面部分是一些书的摘要或注释,主要是便于自己理解,会凌乱一些。
Backbone代码总共1000多行,是一个轻量级的Javascript库,不是完整的框架,各个部分很独立,可以单独应用在你的工程中,也可以跟其他框架协作,比较灵活。看Backbone的官网就会发现,网页布局跟Underscore非常类似,尤其是左树,所以从这个角度上说,Backbone本来就是要当Utility来用的。
Backbone只提供最基本的数据结构(models,collections)和用户接口(views,routes),本身没有UI控件,不限制模板引擎,可以直接用它来实现简单的WEB应用,或者结合其他重一些的框架亦或在它基础上进行增强来实现复杂的功能。
SPA与传统的WEB Page有什么区别,最主要的是在第一次进行页面加载后,后续的导航和请求不必重新加载整个页面,而只需要通过AJAX请求得到需要更新部分的数据,然后部分地刷新页面。SPA仍旧能够利用浏览器的History API,不同视图对应不同的URL,进行可以保存为书签或者前进后退。
这本书中样例代码的命名规范,一个比较好的实践是所有jquery对象都以$开头,以区别于普通javascript对象,例如el和$el。

MV*

Backbone如何实现数据和视图的绑定呢?一方面,一个view可以将它的render()方法绑定到model的change事件,在模型变化时自动刷新视图。另一方面,当view发生变化时,需要call model的方法来保存变化。也就是视图发生变化时要主动更新模型,而模型发生变化时视图要通过监听事件来被动响应。在Backbone的术语中,有Model和View,但没有Controller,事实上,Backbone的View包含了controller的功能,Routers用来管理应用状态。更进一步地分析,发现Backbone实现了自己的MVP*架构,更像是MVP模型。 


在MV*框架中,模板引擎扮演了很重要的角色,模板的重要作用在于避免了拼凑大段html字符串,让页面各部分能够解耦,层次更加清晰,通过数据来驱动页面展现。常见的模板引擎像Mustache,Handlebars,不但能够完成基本的变量替换,而且可以包含复杂的逻辑,如if、else、for, 甚至模板的继承,这能够让UI展现和业务逻辑进一步地得到分离。

Model

Model就是一个数据容器,包含应用程序的数据及相关逻辑。
当一个Model被实例化时,Initialization()方法会首先被调用。事实上,Backbone提供的4个基本类Model、Collection、Router、View中都有一个initialization()方法,可以用它来进行一些初始化工作,比如事件绑定。 
var Todo = Backbone.Model.extend({
    defaults: {
        title: '',
        completed: false
    },
    initialize: function () {
        console.log('This model has been initialized.');
        this.on('change:title', function () {
            console.log('Title value for this model has changed.');
        });
    }
});
var myTodo = new Todo();
myTodo.set('title', 'Check what\'s logged.');
Todo Model定义了两个默认属性,title和completed,通过set方法修改属性时会触发change事件。Backbone支持监听单个变量的事件,比如上例中的“change:title”,只监听title属性的变化,不管completed属性。
如果实现了validate()方法,在model的save()或者set(value,{validate:true})时,会自动调用validate()进行验证。

View

View代表DOM中的一部分UI,比如sidebar,panel,或者是包含整个应用的外框架等。将整个网页分开定义为多个View,可以灵活控制各部分的渲染,响应模型事件。
View有model/collection属性,可以在构造函数中将模型数据传进来,如: 
var myTodo = new Todo({
    title:'Read the whole book', 
    id: 2
});
每个View都有一个el属性,指向当前View所代表的DOM元素,同时,View还有一个$el属性,二者的区别就在于后者是jQuery对象,有show(), hide()等方法可以调用。view.$el等价于$(view.el)。
将el关联到DOM有两种方式,
方法1:新建一个View并指定html标签类型,然后将View对应的元素添加到DOM中 
var TodosView = Backbone.View.extend({
    tagName: 'ul', // required, but defaults to 'div' if not set
    className: 'container', // optional, you can assign multiple classes to
// this property like so: 'container homepage' id: 'todos', // optional
});
var todosView = new TodosView();
console.log(todosView.el); // logs <ul id="todos" class="container"></ul>
后面可以$ele.append(todoVIew.el)添加到DOM中
方法2: 通过CSS选择器添加到页面已有的元素上 
var todosView = new TodosView({
    el: $('#footer')
});
实际情况中,View表示的html不可能这么简单,可以通过模板来实现复杂的渲染,这里包括两部分:模板和渲染(render)。前面说过Backbone并不限定模板引擎,所以可以选择任意自己喜欢的模板引擎,但本书样例采用Underscore的内置引擎。
app.TodoView = Backbone.View.extend({
    //... is a list tag.
    tagName: 'li',

    // Cache the template function for a single item.
    template: _.template($('#item-template').html()),

    initialize: function () {
        this.listenTo(this.model, 'change', this.render);
    },

    render: function () {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});
定义一个模板属性,名字任意。上例中用Underscore的template(),通过jQuery选择器定义了一个模板,模板代码在html文件中。render是实现BackBone的View通常要重写(override)的一个方法,但Backbone自己并不自动调用这个方法,一般是在initialize方法中通过监听model的“change”来触发重新render。
render方法的一个约定是在最后返回this,便于view的嵌套和统一渲染。
View还包括一个events属性,用于定义DOM事件和响应函数的map关系,定义中可以支持选择器,格式如下:'eventName selector': 'callbackFunction'。 
events: {
    'click .toggle': 'toggleCompleted',
    'dblclick label': 'edit',
    'click .destroy': 'clear',
    'blur .edit': 'close'
}
这种声明方式定义的代理事件不必关心DOM元素是否已经渲染完成,而用on()或bind()方法时就要保证DOM元素已经存在。
有一点需要注意的是,上面两种方式的回调函数中this表示的对象不同,在events声明方式中,this指的是View对象,而通过jQuery的on()或bind()方式,this指的是触发事件的DOM元素,这时$(this)是对this的jQuery包装。
将View从DOM中删除可以调用remove(), 默认同时会call stopListening()。

Collection

Collection是Model的集合,在定义collection时一般要指定包含的Model类型。Collection实际主要包括一个model的数组,所以能够想到的数组的操作函数和Underscore的部分函数,collection都支持,如add, remove, map, reduce, find等。数组变化时会触发响应的事件,这里不细说了。 
var Todos = Backbone.Collection.extend({
    model: app.Todo,
});

Events

Backbone的Events用起来很方便,所有Backbone主要的类中都包含了事件功能,比如:Backbone,Backbone.Model,Backbone.Collection,Backbone.Router,Backbone.History,Backbone.View。
Backbone.Events可以让任意对象拥有监听和触发事件的能力。 
var ourObject = {}; // Mixin
_.extend(ourObject, Backbone.Events);
// Add a custom event
ourObject.on('dance', function (msg) {
    console.log('We triggered ' + msg);
});
// Trigger the custom event
ourObject.trigger('dance', 'our event');
基本方法除了on(), off(), trigger(),还有listenTo()和stopListening(),后者可以让a对象监听b对象的事件,如
a.listenTo(b, 'anything', function (event) {
    console.log("anything happened");
});

Routers

路由将URL和回调函数映射起来,任何需要保存为书签、需要共享、需要前进后退的网页都需要有对应的URL。Backbone支持多种路由规则,能够匹配各种不同的URL并提取URL参数传递给回调函数。参见样例: 
var TodoRouter = Backbone.Router.extend({
    /* 定义URL和回调函数的映射 */
    routes: {
        "about": "showAbout",
        /* Sample usage: http://example.com/#about */
        "todo/:id": "getTodo",
        /* This is an example of using a ":param" variable, which allows us to match any of the components between two URL slashes */
        /* Sample usage: http://example.com/#todo/5 */
        "search/:query": "searchTodos",
        /* We can also define multiple routes that are bound to the same map function, in this case searchTodos(). Note below how we're optionally passing in a reference to a page number if one is supplied */
        /* Sample usage: http://example.com/#search/job */
        "search/:query/p:page": "searchTodos",
        /* As we can see, URLs may contain as many ":param"s as we wish */
        /* Sample usage: http://example.com/#search/job/p1 */
        "todos/:id/download/*documentPath": "downloadDocument",
        /* This is an example of using a *splat. Splats are able to match any number of URL components and can be combined with ":param"s*/
        /* Sample usage: http://example.com/#todos/5/download/todos.doc */
        /* If you wish to use splats for anything beyond default routing,
         it's probably a good idea to leave them at the end of a URL;
         otherwise, you may need to apply regular expression parsing
         on your fragment */
        "*other": "defaultRoute"
        /* This is a default route that also uses a *splat. Consider the default route a wildcard for URLs that are either not matched or where the user has incorrectly typed in a route path manually */
        /* Sample usage: http://example.com/# <anything> */,
        "optional(/:item)": "optionalItem",
        "named/optional/(y:z)": "namedOptionalItem"
        /* Router URLs also support optional parts via parentheses, without
         having to use a regex.  */
    },
    showAbout: function () {},
    getTodo: function (id) {},
    searchTodos: function (query, page) {},
    downloadDocument: function (id, path) {},
    defaultRoute: function (other) {}
});
hashchange事件会在页面URL中的片段标识符(第一个#号开始到末尾的所有字符,包括#号)发生改变时触发,Backbone.history.start()告诉Backbone开始监听浏览器的hashchange事件(如果浏览器支持的话)。如果只想修改URL而不触发hashchange,可以用navigate()方法。

Sync API

前面没有讲到的一点是,Backbone的Model和Collection都支持通过RESTful API来获取或者持久化数据,而这些方法的背后实际上是sync()方法。每次Backbone试图read、save或者delete model时都会调用sync()。
根据需要可以在自己的Collection或者Model中重写sync(),从而使用不同的RESTful API后端或增加自己的逻辑代码。 
Backbone.sync = function(method, model, options) {};
有一些现成的Backbone插件实现了不同的sync后端,比如保存在localStroage的或者Redis的。

Backbone基础大概就这些了,后面会抽时间总结一下Backbone.Marionette以及与requireJS的结合。 



---------------- backbone开发经典 336页清晰完整版pdf-------------- 内容简介 · · · · · · backbone.js提供了一套web开发的框架,为复杂javascript应用程序提供一个mvc结构。 《backbone.js应用程序开发》详细介绍了如何使用backbone.js完成web应用开发。全书从了解mvc、spa和backbone.js的基本知识开始,然后着手构建3个示例应用程序;此外,backbone和grunt-bbb、jquery mobile等开发工具的配合使用,以及jasmine、qunit和sinonjs等测试解决方案。 本书的作者是知名的javascript专家、谷歌chrome团队的工程师addy osmani。本书适合于javascript程序员、web开发人员,尤其是想要学习和使用backbone.js的读者阅读参考。 如果你想使用单页应用程序(spa)模型创建前端站点,本书向你展示了如何使用backbone.js完成这类工作。你将学会使用backbone自有风格的模型-视图-控制器(mvc)架构,来创建结构化的javascript应用程序。 《backbone.js应用程序开发》先从了解mvc、spa和backbone的基本知识开始,然后着手构建示例应用程序——一个简单的todo列表应用程序、restful风格的图书应用程序、以及使用backbone和requirejs的模块化应用程序。本书的作者是谷歌chrome团队的工程师addy osmani,他还演示了框架的高级应用。 《backbone.js应用程序开发》包括以下内容: 了解backbone.js如何给客户端带来mvc方面的好处; 编写易于阅读的、结构化的和易扩展代码 ; 使用backbone.marionette和thorax扩展框架; 解决使用backbone.js时会遇到的常见问题; 使用amd和requirejs将代码进行模块化组织; 使用backbone.paginator插件为collections数据分页; 使用样板代码引导新的backbone.js应用程序; 使用jquery mobile,并解决两者之间的路由问题; 使用jasmine、qunit和sinonjs对backbone应用进行单元测试。 作者简介 · · · · · · Addy Osmani,本书的作者是知名的JavaScript专家、谷歌Chrome团队的工程师,他对JavaScript应用程序架构有着强烈的爱好,还创建了一些比较流行的项目,如TodoMVC,并且对Yeoman、Modernizr和jQuery等其他开源项目也有重要贡献。除此之外,Addy Osmani也是一位高产的博主(http://addyosmani.com/blog),同时也是O'reilly出版的《JavaScript设计模式》一书的作者。 徐涛(网名:汤姆大叔;微博:@TomXuTao),微软最有价值专家(MVP)、项目经理、软件架构师,擅长大型互联网产品的架构与设计,崇尚敏捷开发模式,熟悉设计模式、前端技术、以及各种开源产品,曾获MCP、MCSE、MCDBA、MCTS、MCITP、MCPD、PMP认证。《JavaScript编程精解》、《JavaScript启示录》译者,博客地址:Http://www.cnblogs.com/TomXu。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值