注:小编是一个有轻微处女座情节的人,所以很多内容为了方便阅读和格式美观,我都做成了图片,上传后头条都压缩了图片质量,但是不影响大家阅读,因为可以点击放大,哇哈哈!
上一篇《浅谈HTML5单页面架构(一)》探讨了angular+requirejs的一个简单架构,这一篇继续来看看backbone如何跟requirejs结合。
相同地,项目架构好与坏不是说用了多少牛逼的框架,而是怎么合理利用框架,让项目开发更流畅,代码更容易管理。那么带着这个目的,我们来继续探讨backbone。
首先,来看看整个项目结构。
跟上一篇angular类似,libs里多了underscore和zepto。三个根目录文件:
index.html:唯一的html
main.js:requirejs的配置,程序的入口
router.js:整个app或网站的单页面路由配置
第一步,还是建立单页面唯一的HTML
backbone没有在dom属性上做文章,我们还是按原生的或者说熟悉的方法写东西。这里定义了一个container div作为backbone的视图。
然后引入requirejs,data-main表示主程序入口。
第二步,配置main.js
关于requirejs的语法,还是不多说,大家自己去官网看吧。这个是基础。
使用backbone,不得不强调requirejs的shim配置。backbone这个土匪,要的东西多了,要给他“鞋”(underscore),还要给他美金$(jquery)。
由于终端使用jquery就太庞大了,所以这里做了个小把戏,用zepto充当jquery,骗了土匪一把。用几张越南盾,戏称是美金,没想到土老冒也信了。
有个地方需要注意的是,
无论在哪里用requirejs引入backbone后,就会多了Backbone和$这两个全局变量,所以后续再使用backbone就不需要拘束于requirejs的AMD写法了。适当放松透透气也是好的。
配置好依赖关系后,就可以引入router,并调用关键的
开始路由监控。
第三步,配置router,路由表
Backbone.Router.extend这个语法,相信就不必多说了,说多了也说不清楚,大家去官网才是王道:http://backbonejs.org
backbone的路由写法跟angular类似,但对于可选参数的写法是不一样的。angular使用:param?的方式,而backbone使用(:param),哪个方式好,见仁见智吧。
这里定义了一个默认路由,和两个业务路由。
原理很简单,就是遇到module1的哈希(hash)就执行后边这个字符串对应的函数
估计大家早就知道这个玩意。而上述代码中,关键不同点是,这里利用了requirejs做了模块化,路由跳转后做的所有逻辑都在另外的js中定义。
关键的关键,这里使用了url,而且是独立变量的方式配置模块的js,而不是
目的是grunt做requirejs打包时,能切断两侧的js,不要合并在一个大js中。
再另外,大家可以善用一下router.on('route', function)这个接口,及时做一下事件解绑和一些清理工作。
第四步,写一个简单模块
controller1.js
view1.js
tpl.html
模版的写法跟angular不一样,采用的是更普遍的方式,jquery、underscore都是这个模式。简单说就是<%%>包括js代码,用=等号可以直接输出变量值。
这里做了最简单的MVC,M只是一个值name,C就是controller了,V就是view1。
View1的写法需要遵循Backbone的语法,不然这里用Backbone就没意义了。el指向对应的视图dom元素,用的是css选择器,在View中可以使用this.$el获取到这个jquery风格变量。render是自定义的函数。
到这里,运行程序,就能看到module1的效果了。
第五步,再写第二个模块,严格MVC的
model2.js
Model的语法也是遵循Backbone要求了,defaults是默认属性值。读写这些属性,需要通过model.get/set接口,否则就是用toJSON返回整个对象,再不然就解剖式的使用model.attributes.xxx。
fetch是自定义方法,模拟http请求,这是很常规的做法了,不过这个例子没使用backbone的rest化接口。
数据返回后,使用backbone内建的trigger触发事件,通知监听者,也就是view层了。backbone跟angular最大区别就是,backbone不关注view层的组件化,更关注的是model和事件机制,而angular则不重点提事件机制,采用双向绑定把数据更新的破事隐藏起来。各有各的好处,见仁见智吧。
view2.js
接着,我们看看backbone一个典型视图怎么玩。先看initialize方法,这个是new View2()时先执行的初始化逻辑。
我们在这里监听nameEvent这个消息,也就是model2抛出的事件。收到这个通知,就更新界面。逻辑很简单。
这里有一个比较好用的events,交互事件代理机制。
我们不需要单独的写zepto on对dom分别绑定事件,只需要在这里配置一个events映射表即可。
click button等同于zepto的
这里绑定的就是clickSpan事件。
这个事件代理机制,好处是,在路由切换的时候,可以轻松移除事件监听。
tpl.html
controller2.js
controller负责的做的事就是揉合数据,放到view中。先让view用默认数据渲染,再让model去拉取最新数据,最后通过事件机制更新界面。
当然,这个controller并不是backbone规范,大家可以尽情发挥。
最后回到路由表中,当hash变成module2时,就执行:
至此,简单的requirejs+backbone框架已经完成了。除了router的耦合度很高外,每个模块逻辑代码都已经独立,app可以轻松实现按需加载。
那么追求机制的骚年,要停下来吗?按这个方案,在实际开发中,多人经常会在router这个节骨眼上打架,这里的配置化还不够完美。
第六步,优化router,彻底配置化
现有方案的问题是,router中除了写路由配置外,还需要添加相应的function,这样既冗余又容易冲突,那么能否监听route事件,做一个统一的路由处理器?一个处理函数,处理全部路由响应。
上述代码,把路由表抽离,目的是可以放到index.html中,可以在服务器做直出,保持0缓存,轻松实现对外网版本的控制。
另外Router中,没有了每个路由对应的函数,而路由表中的key/value改为真正意义的一个字符串——模块路径。
感谢backbone的健壮,我开始还以为这样肯定会报错,结果backbone没找到对应函数就停止执行了,不错,赞一个。
没有了一个个的相应函数,取而代之的是route事件处理器。
处理器中,利用了配置表的value,拉取对应的模块,并调用相应的controller。有了这个小把戏,大家可以自由发挥了,配置成各种字符串,多个controller集合在一个requirejs模块中等等。。。
另外,这里约定controller中有onRouteChange的接口,用于接收路由切换的通知,好做一些销毁工作。
来看看新的controller代码:
至此,大功告成,多人开发中,需要修改路由,只需要修改一个配置,不在这里写任何逻辑,利用svn合并功能,轻松完成协同开发。
本文代码Github地址:https://github.com/kenkozheng/HTML5_research/tree/master/BackboneRequireJS
欢迎期待下一篇:requirejs+自建路由,抛弃backbone和angular,回归最本真