为什么学习backbone
?这是个好问题。在这个前端框架爆炸的年代,比起backbone
,对开发来说有更多更好的选择,react
,vue
,angular
等等。但这些在使用这些框架的时候,心里却总是有写不踏实的感觉。MVVM
双向绑定是怎么实现的?Virtual DOM
,diff
算法在react
里面是怎么实现的?大框架不好的地方就是,对于新手来说,真正认识其中的原理很不容易。原理不会变,而API
是会变的。
所以我决定静下心先学习backbone
,并认真地研究其中的原理。(这里挖一个坑。这几天在读backbone
和underscore
的源码,争取写一篇源码解析的文章。)
学习框架最好的方式就是写一个应用出来,于是乎我就写了一个应用来练手。中间浪费了很多很多时间在完全没有意义的纠结上面,所以最后是花了很长时间才搞定的。
这一篇文章是一个总结,比较个人化。主要是说说收获和经验。
应用的功能
这个应用是一个个人简历生成器。前端用backbone
+ jquery
+ underscore
+ webpack
+ scss
,后端用express
。可以通过浏览器界面填写个人相关信息,把数据发送到后端,用nodejs
来负责存储数据,生成静态文件。具体详情可以见github
的地址。
相关学习资料
backbone
属于典型的,学的时候觉得很简单,自己写的时候一脸懵逼的框架。相比react
,react
学的时候觉得难度不小,而且加上redux
之后对一些函数式编程的东西偶尔会感到很费解。可是跟着redux
官网推荐的教程慢慢看之后还是能够比较轻松地写出一个过得去的应用的。然而backbone确是相反,学的时候觉得不难,真正写的时候却感觉很多东西很难控制,而且要思考的地方比用大框架多不少。
比较好的教程有这一个。这个教程里面讲了mvc
的基础知识,backbone
的基础知识,以及一个todo
应用,一个带后端的应用还有一个和用requirejs
进行模块化处理的应用。根据这个教程老老实实过一遍其实大致就差不多了(然而我只看了todo
...)。但是要深入理解并用backbone
写大应用还是有不小难度的。
模块化
这里用了webpack
。说实话,webpack
真的是神一般的存在。AMD
也好,CMD
也好,ES6
模块化标准也好都可以轻松打包压缩,还有很多很方便的插件,不能再爽...backbone
的年代(说的好像很久远了一样...)可能模块化还不算特别普及,所以很多例子也是用script
引入的。这样的命名污染问题自然显而易见。而且发出多次请求也会影响性能。因此通过模块化打包还是非常必要的。
model和collection
在backbone
中,model
和collection
相对于view的代码量是小很多很多的。在里面主要处理的是view
所呈现的数据。而且比较重要的是这个model
会与后端数据库的数据类型有很密切的联系。一般来说,后端的接口要求“比较RESTful
”,前后端以json
作为数据传递的方式。这样,从后端获取数据的时候fetch到处理也比较容易,本地的数据save
到服务器的数据处理也会更加自然(都是json
)。当然这也不是硬性规定的,用传统的jquery
的ajax
也是可以的,但这可能就违背了backbone
原本的初衷了(?)。在本人写的这个应用里面就没有遵守这一点。后面会说明原因。
model
里面常常要覆盖的是default
,initialize
方法,如果需要对save
以及fetch
的数据做特殊处理,则需要重写toJSON
和parse
方法。
在我的这个应用里面,存储的数据结构比较特殊。简历有几个小的嵌套的collection
,有几个不是collection
的attributes
,算是比较复杂的结构。因此重写了toJSON
和parse
方法,但是后来没有用到重写的parse
方法,因为发现直接用jquery
的ajax
更加方便直接。重写的方法见这个链接,具体来说就是当需要保存并post
数据到数据库的时候,把model
的属性(这个属性不是attributes
里面的那个,而是真正意义上的属性,类似a.b
)解析为json
结构,然后再保存。而fetch到数据的时候就用得到的数据(一般是json
)初始化几个collection
,然后直接加到model
的属性(a.b
这种形式的属性)中去。
。其实这种情况应该是用backbone-relational
之类的库来解决的,但能解决问题就好。
在view
中,collection
和model
会根据用户操作view
而发生变化,而变化之后又会影响view
的数据呈现。而关于model
和collection
的其他操作还有一些增删查改之类的可以在具体情况下使用。这些操作也是常常写在view
中的。
view
view
是一个大块头,这个应用中view
占了代码中最大的部分。
view
,顾名思义,就是视图,数据的呈现。这里常常和模板配合。在写后端express
应用的时候,因为ejs
有include
的功能,因此模板就被切成了一小块一小块,这样可以避免html
主文件过大。但是backbone
的很多例子都是直接一大块一大块地把模板塞到html
主文件里面。明显不利于维护。因此参考了别人的代码,写了下面的辅助函数,把视图的html
文件读取进来,并添加到相应的view的模板里面,全部读取完之后就调用回调函数。代码如下:
var loadTemplate = function(views, callback) {
var deferreds = [];
$.each(views, function(index, view) {
if(require('./views/' + view)) {
// 把异步事件,即从文件中读取html文件的函数,压入deferreds中
deferreds.push($.get('../tpl/' + view + '.html', function(data) {
// 修改相应的view的template
require('./views/' + view).prototype.template = _.template(data);
}));
} else {
alert(view + " not found");
}
});
// 把所有异步操作都完成之后才调用callback
$.when.apply(null, deferreds).done(callback);
}
用这种方法来处理html
过大问题不算最好的方法,但是在这里能够很好地解决问题。
在view
中主要的是events
,render
,initialize
。在initialize
里面可以通过listenT
o来监听一些model
事件。
有时候在写initialize
的时候要自己render
,但很多情况下不需要。render
函数里面返回this
之后,可以在其他地方,比如router
中把model
注入模板中然后把render
返回的html
直接插入页面中。
var view = new formView({});
this.$container.html(view.render().el);
router
backbone
的路由功能非常方便。在我的app
应用里面整个程序的入口就是router
的函数。可以通过不同的url
绑定不同的函数,在函数中调用视图的函数来实现不同路由的不同的html
片段。
然而在实际操作的时候,可能是因为个人能力还不够,有一个困难从头到尾都无法解决。后来用了比较不好的办法勉强替代了。问题如下:假如要定义路由'/:tab/:filename'
,每次路由发生改变的时候都会调用路由函数。如果在这个路由函数里我新建了一个model
实例,那当我想改变tab
的时候,就不得不重新触发路由函数,重新新建model
。然而我希望model
能够不发生变化,因为tab
是在filename
文件的前提下的标签页,不能换一个标签就重建一个model
。这样做要如何实现呢?想了三天到最后我还是放弃了...(也有可能自己想的需求是有点奇葩..)
其他小收获
原来直接window.print()
就可以调用浏览器打印功能了呀。有个小收获就是有关浏览器打印的尺寸问题,要根据A4
的比例和边距做调整,然后确定页面中心的区域的比例。第二个收获就是发现淘宝的那个icon
库 iconfont 真的非常不错,用起来也非常方便,以后可以去借一下素材hhh
遗憾
这个小应用还是有很多不足的地方
UI
不足,简历页面设计不足。(想爆了脑袋都不知怎么搞比较好看...)功能还不够强大,如果很多内容填写能够更加细致就好了...
如果要部署
github
的话,还不够方便。node
里面生成页面那里用了一些拼接字符串的方法,更加不是很优雅...
总结
通过这个backbone
应用的编写,对于backbone
算是有一个初步的了解了吧。对于MVC
框架也有了一个大体的认识。最近在看backbone
和underscore
的源码,明后天会开始写一篇源码解读的文章,总结一下backbone
里面值得学习的地方。(现在还在看,觉得history
里面对于浏览器兼容的考虑处理挺有意思,Events
要看点设计模式的东西)。这个暑假的最后就慢慢地看多几遍backbone
,好好学习一个!
在backbone
方面我还是个小白,文中有错误请轻喷,相互学习!谢谢大家!
代码在这里,希望能帮到你~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。