backbone详细解析

 

 

Backbone简介

中文APIhttp://www.csser.com/tools/backbone/backbone.js.html

英文APIhttp://backbonejs.org/

Backbone是构建javascript应用程序的一个优秀的类库。他简洁、轻量级、功能实在。

backbone采用MVC模式,本身提供了模型、控制器和视图从而我们应用程序的骨架便形成。

backbone依赖于underscore,他是一个类库,提供了60多个函数处理数组操作、函数绑定,以及javascript模板机制。

模型

模型是保存应用程序数据的地方。我们可以把模型看做对应用程序原始数据的精心抽象,并且添加了一些工具函数和事件。

我们可以使用Backbone.Modelextend方法来创建Backbone模型:

var Note = Backbone.Model.extend({

     defaults:{

         title:’’//用于定义属性的

       created_at:new Date(); //定义一个时间的对象

     },

initialize: function () { }, //立即执行函数一直处于监听的状态

validate: functin () { }, //验证

});

extend的第一个参数是一个对象,他成为了模型实例的属性;

第二个参数是可选的类属性的哈希,通过多次调用extend可以生成模型的子类,他们将继承父亲所有类和实例属性:

var Note = Backbone.Model.extend({    //实例属性

    instanceProperty: 'foo'}, {    //类属性

    classProperty: 'bar'});

assertEqual(User.instanceProperty, 'foo');

assertEqual(User.classProperty, 'bar');

当模型实例化时,他的initialize方法可以接受任意实例参数,其工作原理是backbone模型本身就是构造函数,所以可以使用new生成实例:

var Note = Backbone.Model.extend({

initialize: function (name) { //模型实例化时会立刻执行       

this.set({name: name});

    }

});

var note = new Note('Mickey');

assertEqual(note.get('name'), 'Mickey');

Ps assertEqual用于判断相等

模型和属性

使用setget方法设置获取实例的属性:

var note = new Note();

note.set({ name: 'Mickey' });

note.get('name'); //Mickey user.attributes;//{name: 'Mickey'}

我们看到其实user.arributes是一个对象字面量,我们不会直接操作他,因为我们使用get/set方法可以进行我们的验证流程。

使用clear方法清除实例的属性

var note = new Note();

note.set({ name: 'Mickey' });

note.clear(); //清除note的全部属性

判断实例是否有某个属性或者返回某个属性

var note = new Note();

note.set({ name: 'Mickey' });

note.has(‘name’)  //判断实例是否有name的属性

note.get(‘name’) //返回note实例的title属性值

我们使用validate方法来校验一个实例属性,默认情况没有任何验证,若是我们需要验证的话:

var Note = Backbone.Model.extend({

validate: function (attr,options) {        

if (!attr.name || attr.name.length < 3) {           

 return '名称不能为空或者小于3';

        }

    }

});

如果属性合法,validate不会理睬之,不合法可以返回错误字符串或者Error对象,校验失败get/set方法就会触发error事件:

var note = new Note();

note.bind('error', function (model, error) {    //错误处理函数});

note.set({ name: 'm' });//给特定集合添加一个错误处理程序note.set({ name: 'm' }, { error: function (model, error) { } });

当然我们也可以在initializa里面添加对错误事件的处理

initialize: function () { 

//当验证失败的时候立即执行该函数

this.on (‘invalid’,function ( model, error ) {

    console.log ( error )

}

   

//其他事件实例

this.on(‘change’,function (model,options) { //当属性发生变化的时候触发该函数

     console.log ( ‘实例属性发生了变化’ )

}

// ‘change’ 改为 ‘change:name’ 则仅当name属性发生变化时触发

   

}

使用hash名为default的对象来指定默认属性,在创建一个实例模型时,任何没有指定值的属性都会被设置为默认值:

var NoteModel = Backbone.Model.extend({

    defaults: { name: 'Mickey'}

});

assertEqual((new Chat).get('name'), 'Mickey');

集合

backbone中,模型实例的数据存放在多个集合中,为什么模型之间要使用独立的集合,其原因有点复杂,但在实际中我们经常这么做(虽然我还没做过)。

针对模型,可以通过扩展backbone.collection来创建一个集合:

var Collection = Backbone.Collection.extend({

model: Node //用于规定node的类型

initialize: function (){} //实例化集合时立即执行的函数

});

在上面的例子中,我们覆盖了model属性来指定与集合相关联的模型(这里是Node模型),虽然这个步骤不是必须的,但是为该集合设置一个默认的模型指向往往能派上大用场。

通常集合会包含单个模型的一个实例,而不是不同模型的多个实例。

在创建一个集合时,可以传递一个模型数组,比如backbone模型,如果定义了一个初始化实例函数,在初始化时就会调用之:

var collections = new Collection([{ name: 'Mickey' }, { name: 'Miki'}]);

另一种方法是使用add方法为集合添加模型:

collections.add({ name: 'Mickey' });

collections.add([{ name: 'Mickey' }, { name: 'Mcikey' }]);

还有一种方法就是先实例化模型再加入

var note1 = new Note ({name:’mickey’});

var note2 = new Note ({name:’Miki’});

var collections = new Collection ([note1,note2]);

当传递一个对象时,如果该对象已经存在,则默认不会覆盖,除非设置merge

//此处假设Note模型中有id属性

var collections = new Collection;

collections.add ({id:1, name:’mickey’})

collections.add ({id:1, name:’miki’}) //此时collections中并不存在miki

collections.add ({id:1, name:’miki’}, {merge:true}) //此时id1的模型的name//miki

一些模型的基本操作

var note1 = new Node({name:’mickey’})

var note2 = new Node({name:’miki’})

var note3 = new Node({name:’js’})

var collections = new Collection ([note1,note2])//新建一个集合

collections.remove(note1) //移除note1 实例 collections剩下note2

collections.reset([note1,note2])//覆盖所有模型 collections此时为note1note2

collections.pop()

//删除最后一个模型 并返回该模型 此时collectionsnote1 push()则相反

collections.shift()

//删除第一个模型 并返回该模型 此时collections删除note1 unshift()则相反

collections.add(note3,{at:1})//note3插入到索引号为1的位置上,也就是第二个元素

collections.set([note1,note2])

//插入覆盖模型,如果元素已经在,如果不同则合并,否则不操作。如果集合中没有该模型,则插入,如果集合中有该模型,参数中没有,则删除模型

collections.get(3) //得到id号为3的模型

collections.at(1)  //得到索引号为1的模型

在为集合添加模型时会触发add事件:

collections.bind('add', function (user) {    //...});//移除一个模型collections.bind('remove', function (user) {    //...});//根据模型id获取模型var note = users.get('moduleId');//集合中模型被修改后出发change事件var note = new User({ name: 'Mickey' });

var collections = new Backbone.Collection();

collections.bind('change', function (rec) {//改变一个记录});

collections.add(user);

note.set({ name; 'Miki'});

控制集合内部顺序

一个集合内部元素顺序可以通过comparator方法控制,该方法的返回值便是你希望集合内部排序的规则:

var Collection = Backbone.Collection.extend({

comparator: function (user) {        

return user.get('name');

    }

});

返回值可以是值或者数字,具体例子我们下次有机会来试试看。

视图

backbone的视图并不是模板,而是一些控制类,他们处理模型的表现。

在很多MVC中视图一般指html或者模板,他们在控制器中处理事件和渲染,但backbone中视图:

视图代表一个UI逻辑块,负责一个简单的DOM内容

var NoteView = Backbone.View.extend({

    initialize: function () { }, //初始化立即执行的函数

    render: function () { }  //渲染函数

});

不管视图有没有被插入页面,每个视图都知道当前的Dom元素,即this.elel是从视图的tagNameclassName或者id等属性中创建的元素,没有这些值el就是空div

var NoteView = Backbone.View.extend({

    tagName: 'li',

className: 'view'});

var userView = new UserView();//结果<li class="users"></li>

若是希望视图绑定到页面上已存在的元素上,直接指定el就好(必须在页面加载后才能指定哦,不然找不到):

var NoteView = Backbone.View.extend({

    el: $('.view')

});//也可以实例化一个视图时传递eltagNameclassName\idnew UserView({ id: 'id' });

渲染视图

每个视图都有一个render方法,默认情况下没有任何操作,一旦视图需要重绘便会调render方法,不同的视图用不同功能的函数来覆盖函数,以处理模板渲染,并使用新的html来更新el

var NoteView = Backbone.View.extend({

    template: _.template($('#tmpt').html()), //使用的模板

    render: function () {   //渲染操作

        $(this.el).html(this.template(this.model.toJSON()));       

 return this;

    }

});

backbone本身并不知道我们是怎么渲染视图的,我们可以自己生产元素也可以使用模板类库(一般用这个)。

在前面的代码中,我们使用了this.model的本地属性,他指向一个模型实例,在实例化时传递到视图中,模型的toJSON方法实际上返回模型未加工时的原始属性,可以在模板中使用:

new NoteView({ model: new Note });

委托事件

通过委托,backbone的视图提供了一种添加事件到el的简单快捷的方法:

var NoteView = Backbone.View.extend({

events: {        

'change input[type=checkbox]': 'toggleDone',   

// 绑定的事件如果改变input[type=checkbox]则触发toggleDone函数

'click .destroy': 'clear'

//如果点击.destroy 则触发clear函数

    },

    toggoleDone: function () { },

    clear; function () {}

});

events对象为{'eventType selector': 'callback'}这种格式,selector是可选的,不提供便绑定值el上。

委托利用了事件冒泡机制,意思是可以一直触发而不管el内容是否改变(估计类似于delegate吧)。

上面的callback事件触发时,他的上下文是视图当前上下午,所以this中的this.model/el都可以使用。

绑定和上下文

事实上,每当视图的模型发生变化时,就会触发change事件,然后调用该函数,这就意味这应用程序的视图及HTML和与之对应的模型数据是同步的。

var NoteView = Backbone.View.extend({

    initialize: function () {

        _.bindAll(this, 'render', 'close');      

  this.model.bind('change', this.render);

    },

    close: function () { }

});

需要注意在回调函数中的上下文已经改变,Underscore提供一个函数:

_.bindAll(context, func);

他将函数名字和一个上下文绑定,bindAll保证了所有给定的函数总是在指定的上下文中被调用,因为函数上下文总在胡乱变化,这样做很有用。

模型销毁需要视图绑定delete事件,触发时删除el即可:

控制器

backbone的控制器将应用程序的状态和urlhash片段关联在一起,使url地址可分享,也可以作为书签使用。

本质上,控制器是一些路由和函数组成,当导航到这些路由时那些函数便调用:

router : {    

‘notes(/page/:page)’: ’index’, //第一个page为静态的page 第二个是动态参数page

‘notes/:id’: ‘show’, //可以接收id作为show的参数 /#notes/id

‘login(/from/*from)’:’login’ //*作为一个参数,可以表示一个地址里面的多个部分

},

参数以“:”开始,然后是参数名,当路由被调用时,所有参数都会传递到他的函数,*为通配符,和参数一将和匹配的值一起传递到路由的函数中。

路由是以哈希对象中定义顺序倒叙进行解析的,来创建一个控制器吧:

var PageController = Backbone.Controller.extend({      

   router : {    

‘notes(/page/:page)’: ’index’,

‘notes/:id’: ‘show’,

‘login(/from/*from)’:’login’

},        

});

你可能感兴趣的:(backbone)