backbone 学习之小例子一枚
这是一个关于用户列表以及展示用户信息的例子,简单的简直不像话,但这不是重点,重点是学习了如何基本的使用Backbone。首先来看看效果图:
1、默认画面
2、增加(修改)用户画面
3、用户详情画面
下面就是这个小东西的实现部分了:
Backbone本身并没有DOM操作的功能,那咱们肯定离不开DOM操作,这里选择强大的jquery,当然还可以选择像zepto, ender等其他库,另外Backbone还是依赖另外一个也是很出名的库underscore.由于想要方便,这里就没有写后台程序了,用的是浏览器本身的localstorage存储数据,幸运的是Backbone已经给咱们封装好了backbone.localStorage.js,你只需要引用就可以了。
咱们首先需要存储每一个用户数据的model,所以
var User = Backbone.Model.extend({ defaults: { username: '默认用户名' }, initialize: function() { if (!this.get("username")) { this.set({"username": this.defaults.username}); } if (!this.get("userid")) { this.set({"userid": ++userid}); } } });
defaults,默认的一些数据;在初始化的函数initialize中,咱们只是加入了一些简单的判断。
Backbone中有一个Collection集合,是某一类model的集合,咱们这里恰好要用到,因为咱们有一个列表,这是一个整体,咱们用一个Collection来统一管理,更方便,同时也利于做一些事情,例如排序什么的,直接操作集合就ok了。所以咱们这里就创建一个:
var UserCollection = Backbone.Collection.extend({ model: User, // 持久化到本地数据库 localStorage: new Store("users") });
model:某一类型的Model,默认是Backbone.Model,咱们这里由于是一堆User的集合,所以就设置model为咱们上面创建的User。而localStorage,咱们看到它的值是新创建的一个对象Store,这个Store就是咱们之前引用的backbone.localStorage.js封装的对象。至于是怎么样工作的,以后会有对这个文件的分析,咱们目前知道的是这样写,咱们就可以达到咱们的数据能够和localStorage中key是users的某条数据对应就可以了,并且能够达到同步更新的效果。
好,model这块基本是可以了,下面就是咱们的重点View,首先想好,需要多少个View,从效果图中,咱们可以看到,有三个View需要处理,UserListView、UserModifyView、UserView分别对应着列表页、添加(修改)页、用户详情页;既然有列表,那么每一个列表项是不是也应该有一个UserItemView呢?在这里咱们加上了,实际上这个需要看时机需求了。好,下面就让咱们一一实现:
为了方便操作方便,咱们需要一个基础的View类:
var View = Backbone.View.extend({ register:function (state) { this.state = state; return this; } });
register函数只是方便咱们设置state,state其实是一个model。
(1)列表项UserItemView
var UserItemView = View.extend({ tagName: 'li', template: _.template($('#user-item-template').html()), render: function () { this.$el.html(this.template(this.model.toJSON())); return this; }, events:{ 'click .removeUser': 'deleteUser', 'click .viewUser': 'viewUser' }, viewUser: function() { this.state.trigger('viewUser', this.model); }, deleteUser: function () { this.state.trigger('removeUser', this.model); this.remove(); } });
咱们看到了一些参数,这里需要解释下:
tagName: 如果不设置默认是'div',也就是盛放咱们view展示内容的外层容器
template: 利用underscore提供的template接口,可以得到一个函数(以后详细分析)
render: 方便咱们渲染
events: Backbone提供的可以通过键值对的形式绑定事件
剩余两个就是对应的事件处理函数了。
(2)列表UserListView
var UserListView = View.extend({ template: _.template($('#list-template').html()), initialize:function () { var view = this; this.state = new Backbone.Model(); this.router = this.options.router; // 调用fetch的时候触发reset this.collection.unbind('reset');//注意这里不要重复绑定 this.collection.bind('reset', this.addAll, this);//注意这里不要重复绑定 setTimeout(function() { view.collection.fetch(); }, 0); }, render:function () { var view = this; this.$el.html(this.template(this.state.toJSON())); this.state.on('removeUser', function (user) { user.destroy();//为了修改服务端数据 view.collection.remove(user); }); this.state.on('viewUser', function (user) { view.router.navigate('user/' + user.cid, {trigger:true}); }); return this; }, createUserItemView:function (user) { var userItemView = new UserItemView({ model: user }); userid = Math.max.call(null, user.get('userid'), userid);//确保userid会一直从最大的开始增加 userItemView.register(this.state).render().$el.appendTo($('#list')); }, addAll: function() { this.collection.each(this.createUserItemView.bind(this)); } });
(3)添加(修改)UserModifyView
var UserModifyView = View.extend({ template: _.template($('#modify-template').html()), initialize:function () { this.router = this.options.router; }, render:function () { var view = this; if (this.model) { this.$el.html(this.template(this.model.toJSON())); } else { this.$el.html(this.template({username: ''})); } setTimeout(function() { view.$el.find('input').focus().select();//设置焦点并选中 }, 0); return this; }, events:{ 'click a.add': 'modify' }, modify: function () { var view = this; if (this.model) { this.model.save({'username': this.$el.find('input').val()});//更改服务端数据 } else { this.router.userCollection.create(new User({ username:view.$el.find('input').val(), userid: ++userid })); } this.router.navigate('list', {trigger:true}); } });
(4)最后的详情页UserView
var UserView = View.extend({ template: _.template($('#user-template').html()), initialize:function () { this.router = this.options.router; }, render: function () { var view = this; this.$el.html(this.template(this.model.toJSON())); return this; }, events:{ 'click .editUser': 'editUser' }, editUser: function() { this.router.navigate('edit/' + this.model.cid, {trigger:true}); this.remove(); } });
可以看到,在咱们的View中包含了控制器所干的活了,所以说View中包含的是View和Controller两部分的职责。
咱们到目前为止只是创建了一堆的各种类,咱们需要一个东西来统一管理咱们的小例子,负责一些流程什么的,一般都会选择叫Application,咱们这里简单起见就叫App了。
同时,如果想咱们看到不同的画面的时候意味着地址也跟着改变或者说我点击浏览器的前进后退的时候,也希望能够很好的工作,怎么办呢?这就看咱们的Backbone中的Router的作用了,当然他需要Backbone中的history的支持。
先看咱们的App:
var App = Backbone.Router.extend({ initialize:function (el) { this.el = el; //this.userCollection = new UserCollection([new User({username:'张三', userid: ++userid})]); this.userCollection = new UserCollection(); }, routes:{ '':'list', 'list':'list', 'add':'edit', 'edit/:cid':'edit', 'user':'user', 'user/:cid':'user' }, list:function () { var router = this; this.clean(); this.currentView = new UserListView({ collection: router.userCollection, router:router } ).render().$el.appendTo($(this.el)); }, edit:function (cid) { var router = this, user = null; this.clean(); if (cid) { user = router.userCollection.getByCid(cid); } this.currentView = new UserModifyView({ model:user, router:router }).render().$el.appendTo($(this.el)); }, user: function(cid) { var router = this, user = null; this.clean(); if (cid) { user = router.userCollection.getByCid(cid); } this.currentView = new UserView({ model:user, router:router }).render().$el.appendTo($(this.el)); }, clean:function () { if (this.currentView) { this.currentView.remove(); this.currentView = null; } } });
这里解释下routers: 也是键值对,通过这些规则,来完成咱们的整个流程的控制以及历史的改变。规则是什么呢?这里可以先简单理解下:
'' | hash值为空 |
'list'(...) | hash: #list |
'edit/:cid' | #edit/111 |
很容易理解,不过这里咱们用到的只是简单的规则,在后面对Router进行分析的时候会详细解释这块的。
现在还差最后一步了,实例化咱们的App,然后就万事大吉了
new App('body'); Backbone.history.start();
千万不能忘记了history的支持,只需要调用start方法即可。
欢迎指导、建议。