6

为什么学习backbone?这是个好问题。在这个前端框架爆炸的年代,比起backbone,对开发来说有更多更好的选择,reactvueangular等等。但这些在使用这些框架的时候,心里却总是有写不踏实的感觉。MVVM双向绑定是怎么实现的?Virtual DOMdiff算法在react里面是怎么实现的?大框架不好的地方就是,对于新手来说,真正认识其中的原理很不容易。原理不会变,而API是会变的。

所以我决定静下心先学习backbone,并认真地研究其中的原理。(这里挖一个坑。这几天在读backboneunderscore的源码,争取写一篇源码解析的文章。)

学习框架最好的方式就是写一个应用出来,于是乎我就写了一个应用来练手。中间浪费了很多很多时间在完全没有意义的纠结上面,所以最后是花了很长时间才搞定的。

这一篇文章是一个总结,比较个人化。主要是说说收获和经验。

应用的功能

这个应用是一个个人简历生成器。前端用backbone + jquery + underscore + webpack + scss,后端用express。可以通过浏览器界面填写个人相关信息,把数据发送到后端,用nodejs来负责存储数据,生成静态文件。具体详情可以见github地址

相关学习资料

backbone属于典型的,学的时候觉得很简单,自己写的时候一脸懵逼的框架。相比reactreact学的时候觉得难度不小,而且加上redux之后对一些函数式编程的东西偶尔会感到很费解。可是跟着redux官网推荐的教程慢慢看之后还是能够比较轻松地写出一个过得去的应用的。然而backbone确是相反,学的时候觉得不难,真正写的时候却感觉很多东西很难控制,而且要思考的地方比用大框架多不少。

比较好的教程有这一个。这个教程里面讲了mvc的基础知识,backbone的基础知识,以及一个todo应用,一个带后端的应用还有一个和用requirejs进行模块化处理的应用。根据这个教程老老实实过一遍其实大致就差不多了(然而我只看了todo...)。但是要深入理解并用backbone写大应用还是有不小难度的。

模块化

这里用了webpack。说实话,webpack真的是神一般的存在。AMD也好,CMD也好,ES6模块化标准也好都可以轻松打包压缩,还有很多很方便的插件,不能再爽...backbone的年代(说的好像很久远了一样...)可能模块化还不算特别普及,所以很多例子也是用script引入的。这样的命名污染问题自然显而易见。而且发出多次请求也会影响性能。因此通过模块化打包还是非常必要的。

model和collection

backbone中,modelcollection相对于view的代码量是小很多很多的。在里面主要处理的是view所呈现的数据。而且比较重要的是这个model会与后端数据库的数据类型有很密切的联系。一般来说,后端的接口要求“比较RESTful”,前后端以json作为数据传递的方式。这样,从后端获取数据的时候fetch到处理也比较容易,本地的数据save到服务器的数据处理也会更加自然(都是json)。当然这也不是硬性规定的,用传统的jqueryajax也是可以的,但这可能就违背了backbone原本的初衷了(?)。在本人写的这个应用里面就没有遵守这一点。后面会说明原因。

model里面常常要覆盖的是defaultinitialize方法,如果需要对save以及fetch的数据做特殊处理,则需要重写toJSONparse方法。

在我的这个应用里面,存储的数据结构比较特殊。简历有几个小的嵌套的collection,有几个不是collectionattributes,算是比较复杂的结构。因此重写了toJSONparse方法,但是后来没有用到重写的parse方法,因为发现直接用jqueryajax更加方便直接。重写的方法见这个链接,具体来说就是当需要保存并post数据到数据库的时候,把model的属性(这个属性不是attributes里面的那个,而是真正意义上的属性,类似a.b)解析为json结构,然后再保存。而fetch到数据的时候就用得到的数据(一般是json)初始化几个collection,然后直接加到model的属性(a.b这种形式的属性)中去。

。其实这种情况应该是用backbone-relational之类的库来解决的,但能解决问题就好。
view中,collectionmodel会根据用户操作view而发生变化,而变化之后又会影响view的数据呈现。而关于modelcollection的其他操作还有一些增删查改之类的可以在具体情况下使用。这些操作也是常常写在view中的。

view

view是一个大块头,这个应用中view占了代码中最大的部分。

view,顾名思义,就是视图,数据的呈现。这里常常和模板配合。在写后端express应用的时候,因为ejsinclude的功能,因此模板就被切成了一小块一小块,这样可以避免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中主要的是eventsrenderinitialize。在initialize里面可以通过listenTo来监听一些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的比例和边距做调整,然后确定页面中心的区域的比例。第二个收获就是发现淘宝的那个iconiconfont 真的非常不错,用起来也非常方便,以后可以去借一下素材hhh

遗憾

这个小应用还是有很多不足的地方

  1. UI不足,简历页面设计不足。(想爆了脑袋都不知怎么搞比较好看...)

  2. 功能还不够强大,如果很多内容填写能够更加细致就好了...

  3. 如果要部署github的话,还不够方便。

  4. node里面生成页面那里用了一些拼接字符串的方法,更加不是很优雅...

总结

通过这个backbone应用的编写,对于backbone算是有一个初步的了解了吧。对于MVC框架也有了一个大体的认识。最近在看backboneunderscore的源码,明后天会开始写一篇源码解读的文章,总结一下backbone里面值得学习的地方。(现在还在看,觉得history里面对于浏览器兼容的考虑处理挺有意思,Events要看点设计模式的东西)。这个暑假的最后就慢慢地看多几遍backbone,好好学习一个!

backbone方面我还是个小白,文中有错误请轻喷,相互学习!谢谢大家!

代码在这里,希望能帮到你~


harryfyodor
406 声望32 粉丝

关注前端,关注JS, React, Vue, Nodejs, Backbone.