随着HTML 5的兴起,基于此的项目也越来越的多,因而出现了很多MVC框架,如:Backbone.js、Ember.js、Angular.js等,此系列将阐述这些框架在代码层面的区别,希望可以给初学者一些思路。
本文将介绍Ember.js构建基于jQuery Mobile的PhoneGap项目(Hybird App)。
使用Ember.js + jQuery Mobile + PhoneGap构建一个Hybird App项目,命名为Adobe Reader,具有如下功能:
- 读取http://feeds.adobe.com/xml/rss.cfm的数据(XML源)。
- 将XML的内容以列表的形式展示出来。
- 点击任意列表项目(Item),可以跳转到相应地址。
-
jQuery(1.8.2版)
- 下载地址:http://jquery.com
- 替代JavaScript语法。
-
jQuery Mobile(1.2.0 Final版)
- 下载地址:http://www.jquerymobile.com
- 实现UI构建。
-
Ember.js(0.9.8.1版)
- 下载地址:http://www.emberjs.com/
- 实现MVC架构。
-
Ember-bridge-jqm(0.1)
- 下载地址:https://github.com/kenshin/ember-bridge-jqm
- Ember.js无法直接与jQuery Mobile搭配,所以需要ember-bridge-jqm来“代理”。
- 代码基于https://github.com/LuisSala/emberjs-jqm和https://github.com/tolbard/ember-moving修改。
-
xml2json
- 下载地址:请看范例文件
- XML转JSON的jQuery插件。
-
PhoneGap(2.0.0 版)
- 下载地址:http://www.phonegap.com
- 打包App时需要。
- 附件
- 理解Ember.js的MVC结构特点。
- 掌握Ember.js与jQuery Mobile的特殊写法(ember-bridge-jqm的运用)
- 掌握Ember.js的特色功能:UI Binding。


- common:共同的JS类库,如jQuery、jQuery Plugin等。
- ember:Ember.js相关类库。
- javascripts:Ember.js对应MVC结构的JavaScript代码。
- jqm:jQuery Mobile相关类库。
- phonegap:PhoneGap相关类库。
- stylesheets:CSS文件。

下面将展示Adobe Reader的关键代码,并分别介绍Ember.js中M(Model)V(View)C(Controller)
代码特点、UI Binding
的实现等功能。
Application(应用程序入口)
App = Em.Application.create();
与所有的Ember.js App都是一样,首先要建立一个Application
。
Views(视图)
App.PageView = Mov.PageView.extend();
上面的代码与通常的Ember.js App在View
(视图)层的继承不太一样,主要区别在于全部的View
(视图)都继承自Mov
,那么Mov
是什么?
Mov
定义在ember-bridge-jqm中,可以在assets\www\ember\ember-bridge-jqm.js中找到。为什么Ember.js不能直接与jQuery Mobile一起使用,而非要一个“代理”才行?
因为Ember.js在实现的时候会修改HTML代码,因此具有“侵入性”。众所周知jQuery Mobile也是一个“侵入性”很强的类库,例如:它会把 <div data-role="header"></div>
这样的代码“改为”<div data-role="header" class="ui-header ui-bar-a" role="banner"></div>
。当这两种“侵入性”的类库放在一起,就会出现各种未知错误,所以才有ember-bridge-jqm.js这样的“代理”类库。
通常一个典型的jQuery Mobile具有如下结构:
<div data-role="page">
<div data-role="header"></div>
<div data-role="content"></div>
<div data-role="footer"></div>
</div>
加入ember-bridge-jqm.js后,HTML结构可以直接用JavaScript的方式实现:
App.PageView = Mov.PageView.extend();
App.HeaderView = Mov.HeaderView.extend();
App.FooterView = Mov.FooterView.extend();
App.ContentView = Mov.ContentView.extend();
注:上述代码分别实现了jQuery Mobile的 "page" "header" "content" "footer"视图。
以上就是View
(视图)层的关键代码,主要负责实现jQuery Mobile的结构,接下来看一下Model
(模型)的代码。
Model(模型)
定义如下数据结构(value Object)
:
App.Articles = Ember.Object.extend({
title : null,
link : null,
desc : null,
creator : null,
date : null
});
上述结构与RSS源的XML节点保持一致。由于需要处理XML,因此增加一个叫做ServicesModel
的函数,功能是读取XML,代码如下:
App.ServicesModel = function( target, url ) {
$.ajax({
type : "GET",
url : url,
success: function( xml ) {
//get json
var json = $.xml2json( xml );
//call json2obj
App.Json2Obj( target, json.item );
}
});
}
ServicesModel具有如下功能:
- 使用Ajax方式读取RSS源。
- 将读取的XML转化为JSON对象。
- 调用App.Json2Obj。
再看一下Json2Obj
的实现:
App.Json2Obj = function( target, tmp ) {
target.set( 'content', [] );
$( tmp ).each( function( index, value ) {
var tmp = App.Articles.create({
title : value.title,
link : value.link,
desc : value.description,
creator : value.creator,
date : value.date,
});
target.pushObject( tmp );
});
}
Json2Obj
具有如下功能:
-
循环遍历传入的JSON对象,并将遍历后的内容保存到
VO(App.Articles.create())
。 -
通过
pushObject
将每个遍历后的App.Articles(VO)
保存到target
(传入的参数)。
注:pushObject
是Ember.js方法,与Ember.ArrayController
一同使用,将循环遍历(each
)后的VO
(App.Articles
)保存到(pushObject
)ArrayController
的content
中。
以上就是Model
(模型)层的关键代码,主要负责实现XML的读取、解析和保存,接下来看一下Controller
(控制器)的代码。
Controller(控制器)
App.getArticlesController = Ember.ArrayController.create({
content : [],
init : function () {
//call services model
App.ServicesModel( this, "http://feeds.adobe.com/xml/rss.cfm?query=byMostRecent&languages=5" );
}
});
getArticlesController
具有如下功能:
-
实现Ember.js的控制器
App.getArticlesController = Ember.ArrayController.create();
-
调用
Model
层的ServicesModel
,并传入两个参数:-
this(供使用
pushObject
方法) - URL(传入解析用的RSS地址)
-
this(供使用
总结
以上就是JavaScript端MVC各层的关键代码,代码的实现与其他框架并无太大的区别,需要注意的是如何使用Ember.js方式实现MVC结构的写法:
-
Model:Ember.Object.extend();
-
View:Mov.PageView.extend();
-
Controller:Ember.ArrayController.create();
接下来看一下HTML端的Ember.js写法,并展示Ember.js有别于其他MVC框架的特点:UI Binding
。
HTML端相关类库的引入
<head>
标签内:
<!-- jquery mobile -->
<link rel="stylesheet" href="jqm/jquery.mobile-1.2.0.min.css" />
<!-- customer -->
<link rel="stylesheet" href="stylesheets/style.css" />
<body>
标签内:
<!-- common -->
<script src="common/jquery-1.8.2.min.js"></script>
<script src="common/jquery.xml2json.js"></script>
<!-- jquery mobile -->
<script src="jqm/jquery.mobile-1.2.0.min.js"></script>
<!-- ember.js -->
<script src="ember/ember-0.9.8.1.min.js"></script>
<script src="ember/ember-bridge-jqm.js"></script>
<!-- customer -->
<script src="javascripts/app.js"></script>
<!-- phonegap -->
<script src="phonegap/cordova-2.0.0.js"></script>
注:之所以要把<script>
放到<body>
标签中主要基于加快加载速度的考虑。
jQuery Mobile结构的实现(ember-bridge-jqm的运用)
在<body>
中加入如下代码:
<script type="text/x-handlebars" data-template-name="main"></script>
<div data-role="page"></div>
注:data-template-name
的值一定要设定为"main"
在javascripts/app.js的View
(视图)层中加入如下代码:
App.PageView = Mov.PageView.extend({
templateName:'main',
id: 'page-view',
didInsertElement: function() {
$.mobile.changePage(this.$());
}
});
注:App.PageView.templateName
和id
的值一定按照上述设定。
在HTML端加入如下代码:
{{#view App.HeaderView}}
.....
{{/view}}
{{#view App.ContentView}}
.....
{{/view}}
{{#view App.FooterView}}
.....
{{/view}}
这些代码的结构等价于jQuery Mobile的结构:
<div data-role="header"></div>
<div data-role="content"></div>
<div data-role="footer"></div>
注:App.HeaderView
、App.ContentView
、App.FooterView
定义在javascripts/app.js中,{{}}是模版引擎(Handlebars.js)的语法。
Listview(UI Binding)的实现
在javascripts/app.js新增一个View
(视图)层,命名为ListView
:
App.ListView = Mov.ListView.extend();
在HTML端加入如下代码:(重点)
{{#view App.ContentView}}
{{#collection App.ListView contentBinding="App.getArticlesController"}}
<a {{ bindAttr href="content.link" }} data-ajax="false" >
<h3>{{ content.title }}</h3>
<p>via {{ content.creator }}</p>
<p>{{{ content.desc }}}</p>
</a>
{{/collection}}
{{/view}}
具有如下功能:
-
定义了一个
ContentView
,并在其中加入ListView
。 -
ListView
的contentBinding
设定为App.getArticlesController
,由于ListView
是一个数组结构(App.getArticlesController = Ember.ArrayController.create();)
所以使用了关键字Collection,而非View。 -
content.title、content.creator、content.desc
来源于App.getArticlesController.content
,而App.getArticlesController.content
的值是调用pushObject
而来,通过App.Json2Obj的代码可知,content.title、content.creator、content.desc
分别存储了解析XML后的App.Articles(Value Object)
值。
注:bindAttr href="content.link"
并没有写成:<a href="{{ content.link }}">
,是因为Ember.js在“注入”代码的时候,会生成<script id="metamorph-0-start" type="text/x-placeholder">XXX</script>
这样的结构,所以在设定href
时不能直接写{{ content.link }}
,而是使用bindAttr
方式。
总结
Embe.js通过Collection
和pushObject
以及模版引擎(Handlebars.js)来实现。
PhoneGap
由于PhoneGap只起到打包(Android App)作用,并没有使用PhoneGap的相关功能,因此无需引入<script src="phonegap/cordova-2.0.0.js"></script>
,直接打包即可,PhoneGap的过程略去。
- Ember.js与jQuery Mobile都是具有“侵入性”的框架,所以尽量不要放在一起使用,如在一起使用,需要“代理”方式(ember-bridge-jqm)。
- 由于Ember.js与jQuery Mobile的“不兼容性”,所以在编写jQuery Mobile组件的时候,无法直接写到HTML中,而是写到Handlebars.js的模版里面。(针对动态生成组件而言)
- Ember.js搭配的UI库尽量不具有“侵入性”,例如:Bootstrap等。
-
Ember.js最强大的地方(之一)在于UI Binding。(通过
Collection
和pushObject
以及模版引擎(Handlebars.js)实现) - 如果你对Cocoa熟悉的话,你会在Ember.js身上找到它的身影。
- 如果你对Flex熟悉的话,你会找到久违的“dataProvide”和“itemrender”属性。