本文将从整体思想,数据驱动,事件机制,存在的问题以及实例分析等多个方面来介绍我对backbone的一些认识,里面有很多个人观点,受技术水平和经验的限制,不一定绝对合理,欢迎批评与指正。
1. backbone的整体思想js的作用从大范围上讲,主要包含两个内容,第一是将浏览器的数据与服务器的数据在适当的时候进行相互同步;第二是在用户与浏览器,浏览器与服务器之间的交互过程中,控制页面的变化来反馈不同的功能效果。从技术上来说,前者主要体现为异步请求的处理,后者主要体现为对DOM的操作。大家都知道,这两个方面的东西虽然不难,但是很繁琐,backbone也好,后来的angular react也好,它们诞生都有一个目的就是为了简化这两个方面的工作。不过本文的重点肯定只有backbone了,要说明backbone的作用,得先看下我们不采用backbone的时候是如何处理一个极其简单的编辑页面的,然后再来看backbone的解决方法,通过对比就能看出backbone的一些思想以及给我们工作带来的好处。
这是要演练的编辑页面的原型:
需求如下:
1)这个编辑页面用来处理某个类型的数据,这个类型的数据有两个字段分别是id和name,表示它的唯一标识和名称;
2)当用户选择新增模式打开这个编辑页面的时候,页面初始化状态如原型所示,当用户在文本框中输入非空的文本再点击保存时,页面会把新增的文本传递给服务端,由服务端保存后再返回这个数据的id,同时在文本框下方的文本处显示刚刚保存好的数据的名称;
3)当用户选择编辑模式打开这个页面时,页面会传递一个id以便查出要显示的数据条目,当正确查出了数据的名称之后,把它显示到文本框以及文本框下方的文本中。
4)在用户新增完数据之后,以及从编辑模式打开这个页面时,都可以再次编辑文本框中的内容,通过保存按钮,将最新的名称同步至服务器,成功之后,再在文本框下方显示刚录入的名称。
不采用backbone,我们可能会这样去实现这个页面的功能(提供大概的代码,非完整的):
var $input = $('#new_input'),//输入框 $save = $('#btn_save'),//保存按钮 $name_text = $('#name_text');id = (function () { //获取页面id,详细实现略 })(); if(id) { //编辑时先异步查询数据,再做页面初始化 $.ajax({ url: '/api/data/query', data: { id: id } }).done(function(res){ if(res.code == 200) { var name = res.data.name; $input.val(name); $name_text.text(name); } }) } $save.on('click', function () { var name = $.trim($input.val()); if (!name) return; var params = {name: name}; //编辑时再绑定一个id参数,以便服务器做更新操作 id && (params.id = id); $.ajax({ url: '/api/data/save', data: params }).done(function (res) { if (res.code == 200) { !id && (id = ~~res.data.id); $name_text.text(name); } }) });
这种实现的主要问题在于:
1)在数据变化的时候,必须手工更新DOM,看那两个ajax请求的回调就知道。对这种简单页面可能还好说,要是页面里面包含几十个不同类型的表单控件时,这些页面的更新操作就会变得非常繁杂,而且还容易出错;
2)缺乏封装,没有体现数据的管理,功能都是直接靠请求与DOM操作实现的,实际上按照面向对象的思路以及职责分离的原则,应该把数据的同步和数据的管理功能单独封装起来,把界面变化的功能也单独封装起来,两部分的内容通过接口或者事件来交互。
如果我们把它换成backbone的写法,就会变成:
Data = Backbone.Model.extend({ //定义每个Data类实例的默认值 defaults: function () { return { name: '' } }, //解析异步请求返回的结果,fetch方法与save方法都会调用它 parse: function (res) { return res.data; } }); AppView = Backbone.View.extend({ //指定这个AppView的实例关联的DOM元素 el: 'body', //指定这个AppView实例在做DOM更新时要采用的html模板 template: _.template(document.body.innerHTML), //定义这个AppView内部要注册的一些事件,类似jquery的委托方式注册 events: { 'click #btn_save': 'save' }, initialize: function () { .listenTo(this.model,'change', this.render); this.$input = $('#new_input'); }, render: function () { .$el.html(this.template(this.model.attributes)); return this; }, save: function(){ var name = $.trim($input.val()); if (!name) return; .model.save({name: name}); } }); model = new Data(); AppView({ model: model }); id = (function () { //获取页面id,详细实现略 })(); if (id) { //编辑模式下设置id model.set('id', id); //通过fetch自动发送请求与后台同步 model.fetch(); }
对比前面这两份代码,你会发现,backbone的实现:
1)没有了对ajax请求的直接调用
2)没有了对$new_input以及$name_text这两个DOM元素的直接操作
3)引入了html模板,以便能够快速地更新DOM