导读
最近一段时间,我在研究移动WebApp应用开发,想在设计WebApp前端JS构架时,使用MVC分层技术,经过一段时间的技术选型,最终确定使用 Backbone JS 作为底层基础MVC框架。在使用Backbone写示例时,总是觉得非常怪,但又说不出怪在哪,所以,就想通过Backbone与Ext JS 4 MVC的对比,来发现Backbone的缺点与优化点。PS:由于我在做Desktop前端JS开发时,十分青睐Ext JS这一类的框架,Ext JS 4 MVC是我认为在所有框架中做得最好的。
特性对比
在对比两个MVC框架之前,必须理解四个十分重要的特性:
- UI Bindings:[UI 绑定] 我想说的不仅仅是模板,而是想谈一种在底层模型出现变化时,视图层能够自动刷新的方法。一旦您用过了支持 UI Binding 的框架(例如Ext)就很难放手回头了。
- Composed Views:[组件化视图] 与所有的软件开发者一样,我也喜欢编写模块化、可重用的代码。基于这样的原因,当给 UI 编程的时候,我喜欢使用视图的方法来开发(个人更偏好在模板层时使用),当然这样也就需要拥有足够丰富的视图组件来支持。
- Web Presentation Layer:[web 表示层] 我们在编写 web 程序时想要 Native 风格的组件,但是也没有理由来为一个 web 框架来创建它自己的布局管理器。HTML 和 CSS 是目前解决样式与布局的最好的方法,框架也应该以这一点为核心。
- Play Nicely With Others:[兼容,友好] 不得不承认jQuery是十分犀利的。我不喜欢那种绑定着一个定制的jQuery副本的框架,而推荐直接使用jQuery。
下面是两个框架对于四种特性支持程度的对比
实例对比
咱们先看一个简单的小例子,分别使用Ext JS与Backbone实现。这个例子也是Ext JS 4官方源码中的一个例子,参见
http://dev.sencha.com/deploy/ext-4.1.0-gpl/examples/app/simple/simple.html
对Ext JS 4 MVC不熟悉的同学,可以阅读文章
ExtJS 4 MVC架构讲解
,这篇文章对Ext JS 4 MVC讲解得很透彻。
Ext JS 4 MVC例子
1、目录结构。
2、定义Application。你可以把app当作是一个应用入口,app包含应用的全局配置,同时也负责维护模型、视图、控制器的引用。Ext.app.Application本身也是继承自Ext.app.Controller,具体参见源码与官方文档。
Ext.application({ name: 'AM', // automatically create an instance of AM.view.Viewport autoCreateViewport: true, controllers: [ 'Users' ] });
3、定义Viewport。Viewport中定义了app被加载时的初始视图。
Ext.define('AM.view.Viewport', { extend: 'Ext.container.Viewport', layout: 'fit', items: [{ xtype: 'userlist' }] });
4、定义控制器。Controller是Model与View的粘合剂,Controller的control()函数使用ComponentQuery表达式设置组件视图的事件监听,这样就很好的将视图的控制逻辑与View本身分离,达到View重用的目的,当View是由组件构成时,组件的重用就显得很重要。不熟悉ComponentQuery请参见官方文档。
Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', stores: ['Users'], models: ['User'], views: ['user.Edit', 'user.List'], refs: [ { ref: 'usersPanel', selector: 'panel' } ], init: function() { this.control({ 'viewport > userlist dataview': { itemdblclick: this.editUser }, 'useredit button[action=save]': { click: this.updateUser } }); }, editUser: function(grid, record) { var edit = Ext.create('AM.view.user.Edit').show(); edit.down('form').loadRecord(record); }, updateUser: function(button) { var win = button.up('window'), form = win.down('form'), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); this.getUsersStore().sync(); } });
5、Model和View。在此略过代码部分,对于不熟悉Ext的同学,参见官方的源码及例子。Ext JS的Model、Store和View实现了UI Binding,当Model数据发生变化时自动完成View刷新。在上面的例子中,有一行代码
record.set(values);
,Record就是一个Model实例,当record被赋值时视图被刷新,这部分代码在
ExtJS 4 MVC架构讲解
文中有详细描述。
Backbone MVC例子
1、定义Model。Backbone的Model与Ext Model差不多,但是,Ext Model支持对象依赖关联,这个在特性复杂的企业应用中会比较有用,文章末尾会详细的解释对象依赖关联。Collection与Ext Store在概念上相似,但是,Collection仅仅是一个集合,而Store则是Ext非常重要的一个功能,它是Ext实现数据交互的重要中间件,功能非常强大,有兴趣的同学可以翻阅官方文档。
var User = Backbone.Model.extend({ idAttribute: 'id', url: '../testdata' }); var uer = new User(); var UserList = Backbone.Collection.extend({ url: '../testdata', model: User }); var users = new UserList();
2、定义View。Backbone的View可以结合JS模版引擎实现视图渲染,视图事件控制使用事件委托的方式,例子中,el对象绑定一个委托click事件,当点击tr时调用函数showProfile,具体View事件请参见Backbone官方源码。视图控制事件监听应该出现在Controller中,由于Backbone没有Controller的概念,所以视图控制事件监听在View中实现,与视图渲染混在一起,导致视图无法重用,没有达到MVC分层的目的。现在,我们换一种思路思考,把Backbone View看作是一个Controller,View仅仅使用是一个HTML模版,虽然有点牵强,但视乎也是MVC。Backbone在早期的版本是有Controller这个类,但在新版本将Controller重命名成Router,我想Backbone作者也意识到了这个问题的存在吧!
var HomeView = Backbone.View.extend({ el: $('#mainview'), events: { 'click tr': 'showProfile' }, showList: function() { var html = ['<table>']; users.each(function(uer) { html.push('<tr>'); html.push('<td>', uer.get('firstname'), '</td>'); html.push('<td>', uer.get('lastname'), '</td>'); html.push('</tr>'); }); html.push('</table>'); this.$el.html(html.join('')); }, showProfile: function() { this.$el.html('Hello, ' + uer.get('firstname') + ' ' + uer.get('lastname')); } }); var home = new HomeView();
3、定义Router。Router是个好东西,可以监听地址栏hash改变,实现页面跳转的控制逻辑。这视乎和Controller有点关系,但又能不说Router是Controller,这也是Controller重命名成Router的一个原因。
var AppRouter = Backbone.Router.extend({ routes: { 'list': 'list', 'profile/:id': 'getProfile' }, getProfile: function(id) { uer.set('id', id); uer.fetch({ type: 'POST', data: { id: id }, success: function() { home.showProfile(); } }); }, list: function() { users.fetch({ type: 'POST', success: function() { home.showList(); } }); } }); new AppRouter(); Backbone.history.start();
总结
Ext JS 4 MVC是我认为非常完美的MVC框架之一,但是太依赖Ext的体系,很难从体系中剥离出来。
Backbone JS优点、缺点都比较明显:
- 优点:小巧、轻便可以很容易的集成到应用中,Backbone MVC可以适用大部分的项目需求。
- 缺点:Backbone是MVC的不完整实现,只实现了部分,功能比较弱。
我会考虑增加一些Backbone的功能,如Collection和Model的离线缓存,UI绑定数据改变自动渲染,在业务层封装出类似Ext的MVC,模版与组件化是视图渲染的两种主要方式。
扩展阅读------对象依赖关联
一个Model对象与其他Model对象存在依赖关系。定义两个Model:User、Post,User与Post的关系是一对多。
服务端响应JSON格式:
{ id: 'user1', posts: [{ id: 'post1', user_id: 'user1', }, { id: 'post2', user_id: 'user1', }, { id: 'post3', user_id: 'user1', }] }
使用Ext JS 4定义Model:
Ext.define('Post', { extend: 'Ext.data.Model', fields: ['id', 'user_id'], belongsTo: 'User' }); Ext.define('User', { extend: 'Ext.data.Model', fields: ['id'], hasMany: {model: 'Post', name: 'posts'} });
数据访问:
User.load('user1', { success: function(user){ user.posts().each(function(rec){ console.log(rec.get('id')); }); } });
参考资料
- The Top 10 Javascript MVC Frameworks Reviewed
- Backbone JS框架指南
- Backbone源码分析-Backbone架构+流程图
- ExtJS 4 MVC架构讲解
原创文章,转载请注明出处 http://zhangdaiping.iteye.com