对比多个手机Web应用开发框架搭建和开发流程的优劣

来源:互联网 发布:淘宝店铺转让qc41 编辑:程序博客网 时间:2024/04/29 00:29
关于移动平台及框架开发,是当前非常火的方向,也是大家比较关注的一块。我对此也不是很熟悉,让我们一起来学习。在这个过程中,我会尽可能地搜集资料和大家一起分享关于这块的技术发展状况,同时也会带着大家找到一个比较适用我们的移动开发平台和实例。
当然,这个过程需要大家一起来关注和提出建议。
对于web App的发展,这二年发展非常快,不断地侵蚀Native app的市场,主要由于技术的不断进步,在web app缺点上,如在安全和性能上都得到了拟补,而原先web app优势,又得到了进一步放大:
1)基础的调试工具,现在已经有许多相关的web app的调用工具,但主要还是在pc浏览器上做调试。主要用Chrome/IE/Firefox等都提供强大的从资源文件浏览、CSS应用规则监控、网络请求以及js断点执行的调试工具。
2)UI层组件/框架
1‘ jQuery 可以认为是最强的js Library之一,可以通过大量构建好的Library快速帮助开发者实现各种各样操作DOM以及Css的效果
2’ JQ.Mobi 由移动设备APPMobi推动的,是重写jQuery在移动设备上的实现,大小仅为3K;但速度在android比JQuery快3倍,在ios上快2.2倍
3‘ Sencha Touch则定位于Mobile上的HTML5 WEB APP 开发框架。可以让开发者通过MVC的模式进行web APP开发。
4‘ 在这个领域有许多竞争者,如yahoo!/Zepto/joshfire等等
3)数据和业务逻辑框架
这部分由于涉及具体行业或领域,相关性比较强,移动技术发展刚兴起,所以现在还比较少,据我所知,用友已经开始在做自己的移动开发平台。下面我会查一些,发给大家,如有兴趣可以研究。
4)跨平台的解决方案
这块当前是众多企业试图想进入到移动端开发最为关心的一块,我们也如此。因为进入后要面临移动设备分裂而又很难找到合适的多平台移动开发者等问题。选用本地开发,人力成本会很高,而选用Web App的方案,则在调用本地能力上还不成熟,成功方案太少。当前在这个领域主要有两家:
1)PhoneGap 目前覆盖到IOS、Android、Window Phone BlackBerry、webOS、symbian等平台。当前已被Adobe收购。当前发展速度非常快,已经成为此领域领军。当前sdk版本已经发展到1.7.0,网站是:http://phonegap.com/
2)Titanium 当前也越来越被关注,当前sdk发展到2.0.1,网站是:http://developer.appcelerator.com/blog/2012/04/titanium-mobile-sdk-2-0-1-is-available-now.html
学习资料:http://www.verycd.com/topics/2917085/

3)HTML5的发展,慢慢地会代替当前跨平台sdk

手机web app应用的开发,离不开html5的发展,如果只是在以前的html技术基础上来进行webapp应用开发,则会受到好多局限(这个局限在后面会通过对比说明),对于移动设备上的webapp开发的优势得不到更大的体现。为此,先认识下html5.

html5新特性
离线应用 :代码和数据可以缓存在本地,支持离线运行
音视频 : 支持多种音视频格式,可以在网页中直接播放音视频
绘图能力: 2D/3D(webGL)图形渲染,可以在网页中绘制图形
地理信息 :在网页中获取精确的地理位置
设备访问 :网页中直接访问系统设备,比如摄像头,通讯录等
文件系统访问: 网页中访问文件系统对象,读取文件属性、内容
高级排版 : 支持更多网页排版功能,囿角、颜色渐变、阴影等等
动画特效 : 支持多种的元素变换和移劢,支持创建各种劢画效果
触控输入 : 增加多点触控事件支持,在触屏设备上支持更多的操作
网络增强 : 网页应用可以访问底层网络信息,包括socket、网络连接信息等
消息推送 : 服务端消息推送,桌面消息推送
多线程 : 支持脚本并行执行,提高脚本执行效率
跨域访问 : 支持客户端跨域请求

正是由于这些新特性才能使webAPP优势体现出来,如果没有这些webAPP就无法做本地操作及设备访问等功能。但是html5当前的发展还只是一个热门,各个浏览器商的发展良莠不齐,对这些新特性支持差异也非常大。基于当前的发展,我们把基于移动设备应用开发分为三类:webAPP、Native APP、Hybrid APP.
Web App

  这个主要是采用统一的标准的HTML,JavaScript.CSS等web技术开发.通过不同平台的浏览器访问来实现跨平台.同时可以通过浏览器支持充分使用HTML5特性,缺点是这些基于浏览器的应用无法调用系统API来实现一些高级功能,也不适合高性能要求的场合.

  Native APP

  就是所谓的原生应用.指的是用平台特定的开发语言所开发的应用.使用它们的优点是可以完全利用系统的API和平台特性,在性能上也是最好的。缺点是由于开发技术不同,如果你要覆盖多个平台,则要针对每个平台独立开发,无跨平台特性.

  Hybird App

  则是为了弥补如上两者开发模式的缺陷的产物.分别继承双方的优势.首先它让为数众多的web开发人员可以几乎零成本的转型成移动应用开发者;其次,相同的代码只需针对不同平台进行编译就能实现在多平台的分发,大大提高了多平台开发的效率;而相较于web App,开发者可以通过包装好的接口,调用大部分常用的系统API.

下面再提供一张对比表(限于jira工具不能提供表单):
Native APP、Web APP、Hybrid APP特性对比表:
  Native App Web App Hybrid App
开发成本 高 低 中
跨平台 否 是 是
需要安装 是 否 首次安装
实时增量更新 支持 支持 是
系统API调用 能 否 能
开发灵活度 中 中 高
对AppStore依赖 高 低 低

Native 在这就不谈了,我们主要谈移动设备的webapp及Hybrid APP
这两个现在主要的代表有:
PhoneGap(Hybrid) Titanium jQueryMobile jQTouch Sencha Touch
下面我们对现在比较流行的几个框架进行对比分析:
由于此处不能呈现表单而且内容巨多,所以我上传会附件,请大家下载看吧。

后面,我将再深入到每个框架中具体地和大家分享使用方法。再后会结合几个框架来用一个项目讲解移动设备上的webapp应用开发。欢迎大家一起深入讨论。

主要有两个表:一是三个方向的对比表,另一个是几个当前比较热的框架对比说明表,非常详尽。

最近一直非常忙,其中一部分也是忙于学习和理清一些webApp框架。
根据上次写的相关框架的对比,发现框架之间的侧重点是非常不同的,对于基于web浏览器和本地化的功能框架,像是phoneGap,是构建一个web技术与本地语言之间的桥梁,即让web技术oc之间互联互通。本质上是解决js和本地api之间调用和通信。在ios上,通过本地代码的拦截js中特定代码来解析处理。而像jQuery和sencha等,是纯基于web和html5技术建立的一套手机web应用框架。
由于之前负责过和webApp框架相关的开发项目,基于经验性选择,我选择web技术从sencha着手。主要原因三点:一是sencha在互联网技术中发展非常快,而且现在基本上已经非常成熟。二是sencha文档及经验性稍多一些,学习成本相对低,第三点也是最为重要一点,sencha当前发展出针对手机的框架,sencha touch2是基于MVC思想开发的,而且比之前的1.1版本非常成熟,也非常灵活。

下面我为针对sencha touch2(简写:st2)做相关简介和帮助大家学习:

网上主要以外文资料为主,中文资料非常少。我只找到非常少的一些。

Sencha Touch 2 应用程序简介

注:为方便起见,文中所有出现 Sencha Touch 的地方均以 ST 简写替代。

ST2 专为构建可跨多平台工作的(web)应用程序而优化。为尽可能的方便您编写应用程序,我们提供了一个虽简单但却强大的应用程序架构,它可以视作是对MVC(Model/模型、View/视图、Controller/控制器)模式的扩充。使用这种模式编写应用程序,不仅可以保证你的代码干净、便于测试、容易维护,还能得到如下好处:

访问历史支持:你的应用将获得完整的后退按钮功能,其中的任意部分都可以被链接到
深度链接:如同以往链接到某一个web页面一样,深度链接可以打开你应用程序中的任意屏幕
设备配置文件:共享通用代码的同时,轻松为手机、平板电脑还有其他设备定制应用程序UI(用户界面)

Anatomy of an Application
应用程序结构解析

ST 应用程序是一个由 Model、View、Controller、Store、Profile 组成的集合,外加一些额外指定的元数据,比如 icon 图标和启动界面显示的图片。

数据模型:在应用程序中表示一种数据模型,比如一个电子商务应用程序可能会有用户、产品、订单等不同的数据模型
视图:负责将数据展示给用户,并扩充 Sencha Touch 的内置组件。(译者注:你可以理解为用户界面的一个个组成部分)
控制器:处理应用程序的交互,侦听用户的轻触、猛击(译者注:真实意思是长按?)等事件并做出相应的响应
数据存储器:负责把数据加载到你的应用并以列表(List)或者数据视图(DataView)等形式表现出来
设备配置:可以使你为平板电脑和手机等不同设备,轻易定制应用程序用户界面,并尽可能多的共享代码

Application 对象通常是你开发一个ST应用时需要定义的第一个东西,类似下面这样子:
Ext.application({
name: 'MyApp',
models: ['User', 'Product', 'nested.Order'],
views: ['OrderList', 'OrderDetail', 'Main'],
controllers: ['Orders'],
launch: function() { Ext.create('MyApp.view.Main'); }
});

如代码所示,application 构造参数中有个 name 属性,它为你的应用程序创建一个唯一命名空间,其下包含了该应用全部的 model、view、controller 还有其他 class(类),比如一个叫做 MyApp 的应用就应该遵循以下形式来组织:MyApp.model.User, MyApp.controller.Users, MyApp.view.Main 等,这可以保证你整个的应用程序都处在一个唯一全局变量下,从而最大限度降低代码冲突的可能性。

应用程序会按照你在 application 构造函数中定义好的 models, views 和 controllers 属性成员来自动加载它们对应的 class(类)到当前应用,ST2 架构约定的文件结构如下:model 都放在 app/model 目录,controller 都放在 app/controller 目录,view 则放在 app/view 目录,比如 app/model/User.js, app/controller/Orders.js, app/view/Main.js(译者注:原文中 Orders.js 文件的路径是错的,model、view、controller 这三个目录名称都没有 s )

注意 models 属性中有一个成员的定义跟其他不一样,("MyApp.model.nested.Order"),除了遵循上述的常规命名格式以外,我们还可以使用完整类名的方式来定义这些配置,换言之 application 构造函数中的 models、views、controllers 这些参数属性都可以用完整类名方式来定义,想了解更多关于如何定义依赖项的细节,请参见文章 Dependencies section of the Ext.app.Application docs ( Ext.app.Application 文档中的 Dependencies 章节)

Controllers
控制器

Controller(控制器)就像胶水一样粘合出一个完整的应用程序,它们侦听UI界面触发的事件,然后做出相应的动作,还能够让我们的代码更简洁,可读性好,从而把界面逻辑和控制逻辑隔离开来。
假如你需要用户通过一个 login 表单来登录你的应用程序,此时的 view(视图)就是这个包含了所有字段和其他元素的表单,而它的 controller(控制器)要做的就是侦听表单提交按钮的点触事件并进行身份验证,每次我们要处理数据或者状态的时候,这个 controller(控制器)才是应该起作用的类,而不是 view(视图)。
Controller(控制器)通过一些简单的约定,展示了一套虽小但却很强大的特性。应用程序的每一个 controller(控制器)都是 Ext.app.Controller 的一个子类,当然你也可以继承现有的 controller ,只要它也是继承自 Ext.app.Controller ,controller(控制器)都存在于 MyApp.controller.* 命名空间,例如你的应用有一个叫做 Sessions 的controller ,那么它的完整命名空间将会是 MyApp.controller.Sessions 并且被定义在 app/controller/Sessions.js 文件中。
每个 controller(控制器)都是 Ext.app.Controller 的一个子类,加载它的应用程序也只会对它实例化一次,应用程序自己会控制每个 controller(控制器)在同一时间只有一个实例。我们使用 Application 对象的 controllers 参数来加载 Controller(控制器)并且会自动实例化它们。

A simple example
一个简单例子
这里将演示如何快速定义上面描述的 Sessions 控制器,我们将使用 controller 的两个配置参数,refs 和 control ,refs 是找到页面组件的简单方式,这个例子里 controller(控制器)会搜索所有 formpanel 类型的控件,然后将找到的第一个赋值给 loginForm 属性,这个属性会在下面的 doLogin 方法中用到。
然后我们要做的是配置 control 参数,像 refs 一样使用 ComponentQuery 选择器来查找所有包含 button 控件的 formpanel 下的 button 控件,在本例中,将会得到我们登陆表单当中的提交按钮,任意一个符合此条件的 button 触发了tap事件,controller(控制器)都会去调用其中的 doLogin 函数。
Ext.define('MyApp.controller.Sessions', {
extend: 'Ext.app.Controller',
config: {
refs: { loginForm: 'formpanel' },
control: {
'formpanel button': { tap: 'doLogin' }
}
},
doLogin: function() { var form = this.getLoginForm(), values = form.getValues(); MyApp.authenticate(values); }
});

doLogin 函数本身非常容易理解,由于我们前面定义了一个叫做 loginForm 的 ref ,controller(控制器)将会自动生成一个叫做 getLoginForm 的函数用来返回该 formpanel (也就是这个叫做 loginForm 的 ref 啦),待完成对这个 form 的引用之后,我们只需要把其中的值(用户名和密码)取出来然后传递给 authenticate(身份验证)函数即可,上述就是一个 controller(控制器)能做的绝大部分事情了 —— 侦听事件(通常由UI触发)然后做点别的什么事情,比如用户身份验证。

Stores
数据存储器

Store(数据存储器)是 ST 的重要组成部分,它能够实现大部分的组件数据绑定工作。简单来说,一个 store(数据存储器)就是一个由 Model(数据模型)的实例组成的数组,诸如 List 和 DataView 这类的数据绑定型控件,他们会为 Store(数据存储器)中的每一个 Model(数据模型)实例渲染一个 item(这里指数据绑定控件的子项),Store(数据存储器)中的 Model(数据模型)实例被添加或者删除的时候会触发数据绑定控件的相应事件,从而实现控件的更新。

Stores guide (数据存储器指南)网页上有更多信息,比如到底什么是 Store(数据存储器)以及它们在你的应用程序中是如何去与 Component(组件)协调工作的,那里还有几个你必须注意的特殊要点,均与 Application 实例有关。

Launch Process
Application的Launch

每个 Application 对象都会定义一个 launch 函数,它将会在你应用程序所需的全部 class 加载完成,且应用程序已经做好准备的情况下执行。一般来说这里就是你用来放置应用程序启动逻辑的最好位置了,比如你可以在这里为你的应用创建主要 view 框架。
除了 Application 中的 launch 函数之外,还有两个地方可以放置启动逻辑:第一,每个 controller(控制器)都可以定义一个 init 函数,这个函数将会运行在 application 的 launch 运行之前;第二,如果你使用了设备 profile ,每一个 profile 都可以定义一个 launch 函数,他将会在 controller(控制器)的 init 之后和 application 的 launch 之前被调用。
注意只有活动 profile 的 launch 函数才会被调用,比如你分别定义了 phone 和 tablet 的 profile ,现在是在 tablet 上运行它,那么只有 tablet profile 中的 launch 函数会被调用到。
Controller#init functions called
Controller 的 init 首先被调用
Profile#launch function called
其次是当前 Profile 的 launch 被调用
Application#launch function called
然后 Application 的 launch 被调用
Controller#launch functions called
最后是其他 controller 的 launch 被调用
当使用 profiles 的时候,最好把启动逻辑代码放在 profile 的 launch 里面,因为每个 profile 可能需要调用不同的 view 来构建启动界面。

Routing and History Support
路由和访问历史支持

ST2 具有完整的路由和访问历史支持,SDK 中的好几个例子,包括 Kitchen Sink ,都使用了历史路径支持,以实现通过 back 按钮可以轻易的在屏幕之间回退导航,这一点在 Android 上尤其有用。
Beta 1 中有关于访问历史支持的完整文档,对于 2.0.0 PR4 来说,学习 ST2 的这个功能的最好地方是 Kitchen Sink 例子,这个例子在路由和状态返回等需要访问历史支持的地方有很多说明。

Using Models in your Applications
在应用程序中使用数据模型
来看一下如何创建一个数据模型:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },

{ name: 'name', type: 'string' }

]
});

在最简单的情况下,一个数据模型只是一组字段和数据,我们来看一下Ext.data.Model的四个主要部分,Fields(字段), Proxies(代理), Associations(关联),Validations(验证)。

Using Proxies
使用代理
通常store会使用proxy来处理model数据的加载和存储。Proxy有两种类型,客户端和服务端。客户端类型的例子里包含了浏览器内存存储和html5本地存储两种。服务端代理通过配置远程服务器来获取数据,例子包含了Ajax/JsonP/Rest的方式。

代理可以直接配置在数据模型上,比如这样:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'age', 'gender'],
proxy: {
type: 'rest',
url : 'data/users',
reader: { type: 'json', root: 'users' }
}
});

// Uses the User Model's Proxy(使用User数据模型代理)
Ext.create('Ext.data.Store', { model: 'User'});
这么做有两个好处,首先每一个使用User数据模型的store都通过同样的方式加载数据,这样我们就无需把proxy的配置每个地方复制一遍了。第二我们可以在不需要store的情况下就加载和保存数据。
// Gives us a reference to the User class(实现对User类的引用)
var User = Ext.ModelMgr.getModel('User');
var ed = Ext.create('User', { name: 'Ed Spencer', age : 25});
// We can save Ed directly without having to add him to a Store first because
// we configured a RestProxy this will automatically send a POST request
// to the url /users
// 我们可以直接保存Ed而无需把它加入到Store,因为我们已经配置了一个RestProxy
// 它会自动发送一个Post请求到/users这个url
ed.save({
success: function(ed) { console.log("Saved Ed! His ID is "+ ed.getId()); }
});
// Load User 1 and do something with it (performs a GET request to /users/1)
// 加载User 1然后做点什么(发送一个Get请求到/users/1)
User.load(1, {
success: function(user) { console.log("Loaded user 1: " + user.get('name')); }
});
Associations
关联
数据模型可以通过Association API关联在一起。大部分应用程序都要处理很多不同的数据模型,数据模型之间几乎总是存在关联。一个博客应用程序可能会包含用户、发帖和评论等数据模型。每一个用户创建很多帖子,每个帖子又会收到很多评论。我们可以像下面这样来定义他们的关联。
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name'],
proxy: {
type: 'rest',
url : 'data/users',
reader: { type: 'json', root: 'users' }
},
hasMany: 'Post' // 等价于 { model: 'Post', name: 'posts' }
});
Ext.define('Post', {
extend: 'Ext.data.Model',
fields: ['id', 'user_id', 'title', 'body'],
proxy: {
type: 'rest',
url : 'data/posts',
reader: { type: 'json', root: 'posts' }
},
belongsTo: 'User',
hasMany: { model: 'Comment', name: 'comments' }
});
Ext.define('Comment', { extend: 'Ext.data.Model', fields: ['id', 'post_id', 'name', 'message'], belongsTo: 'Post'});

Validations
验证
ST2的数据模型对数据验证有着丰富的支持。为了演示这些,我们继续在前面创建的例子基础之上进行扩展。首先给User数据模型加入一些验证:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ...,
validations: [
{type: 'presence', name: 'name'},
{type: 'length', name: 'name', min: 5},
{type: 'format', name: 'age', matcher: /\d+/},
{type: 'inclusion', name: 'gender', list: ['male', 'female']},

{type: 'exclusion', name: 'name', list: ['admin']}

],
proxy: ...
});

Using Views in your Applications
在应用程序中使用视图
从用户的视角来看,你的应用程序就是一堆视图的集合,尽管应用程序大部分有价值的东西都在数据模型和控制器里,但视图才是直接跟用户互动的东西。这篇指南将带我们学习如何创建视图来搭建应用程序。

Using Existing Components
使用现存组件

最简单的方法是使用Ext.create来创建一个ST中现存的内置组件,这同样也是一个视图。例如我们只想要创建一个包含简单html代码的Panel,我们可以这么做:
Ext.create('Ext.Panel', { html: 'Welcome to my app', fullscreen: true});

上述例子将会创建一个包含html代码的Panel并使其充满屏幕。你可以用这种方法创建任意内置组件,不过最好的方式还是专门创建一个(内置组件的)子类,然后再来创建这个(定制后的)子类的实例。幸好这也不是什么麻烦事儿。
Ext.define('MyApp.view.Welcome', {
extend: 'Ext.Panel',
config: { html: 'Welcome to my app', fullscreen: true }
});
Ext.create('MyApp.view.Welcome');

上述代码的结果是一样的,但是我们现在有了一个自己命名的新组件,而且以后我们还可以多次使用这个新组件。这才是我们推荐的应用开发模式:创建一个现存组件的子类然后再创建该子类的实例。我们来看一下这种方式跟前面相比有哪些变化:
Ext.define allows us to create a new class, extending an existing one (Ext.Panel in this case)
Ext.define允许我们通过继承一个存在的类(此例中是Ext.Panel)来创建一个新类。
We followed the MyApp.view.MyView convention for our new view class. You can name it whatever you like but we suggest sticking with convention
遵循MyApp.view.MyView的常规来给新视图命名,我们也推荐你这样做,而不要随心所欲
We defined config for the new class inside a config object
我们使用config对象为新类定义它自己的参数

一个更贴近现实的例子

这个视图类来自于我们的Twitter应用:
Ext.define('Twitter.view.SearchBar', {
extend: 'Ext.Toolbar',
xtype : 'searchbar',
requires: ['Ext.field.Search'],
config: {
ui: 'searchbar',
layout: 'vbox',
cls: 'big',
items: [
{ xtype: 'title', title: 'Twitter Search' },

{ xtype: 'searchfield', placeHolder: 'Search...' }

]
}
});

视图在MVC中的用法

我们强烈建议绝大多数ST应用程序都应该遵循MVC的模式开发这样你的代码将会组织良好并且利于重用。作为MVC中的V,视图非常适用于这种架构。MVC中关于视图的规则非常简单并且跟我们上面用到的命名规则毫不冲突。
我们的MyApp.view.MyView类应该在app/view/MyView.js文件中进行定义,这样应用程序就能自动找到和加载它。如果你对“MVC式ST应用程序”的文件架构还不熟悉,你可以这么理解:一个应用程序就是一个html、一个app.js、再加上分处于app/model、app/view、app/controller目录下的一堆model、view、controller文件的集合:
index.html
app.js
app/
controller/
model/
view/
MyView.js

你可以按照你的需要创建很多视图文件放在app/view目录里。通过在app.js中指定application的views参数,他们就能被自动加载了。
//contents of app.js
//app.js内容
Ext.application({
name: 'MyApp',
views: ['MyView'],
launch: function() { Ext.create('MyApp.view.MyView'); }
});

前面我们主要说了MVC中的M和V,而C我们放在最后来说,不仅仅是因为他多重要,更重要是他是纽带。

控制器 [Controller] 在Sencha Touch MVC中起到的是纽带作用,它控制视图 [View] 的展示逻辑,又负责以数据模型 [Model] 为基础的数据 [Data] 处理逻辑(包括数据的加载、展示、更新、增删等等)。控制器就像胶水,有了它才能够把一个Sencha Touch(后面一律简写做ST)应用程序[Application] 的各项元素黏合在一起,使之协调工作并完成预期的任务。

控制器用来响应发生在应用程序中的事件,如果你的应用程序包含一个让用户点击的登出按钮,那么就应该有一个控制器来侦听该按钮的点击事件然后作出恰当的反应。控制器就像胶水,它控制视图类展示数据,并通过数据模型类来加载和保存数据,于是两者就这样被黏合在一起。

控制器与Ext.app.Application的关系

控制器存在于应用程序的上下文环境中,一个应用程序通常有若干的控制器组成,每一个控制器分别负责实现应用程序中某一个特定的功能部分,例如一个处理在线商城销售订单的应用程序就可能会包含订单、客户、产品等控制器。

所有应用程序用到的控制器都在Ext.app.Application.controllers 中进行声明,应用程序会自动实例化每一个控制器并保持对它们的引用,因此我们一般无需自己去直接实例化控制器。按照惯例,每一个控制器的命名应该对应他要处理的事物(通常是数据模型),而且常常是复数形式。例如你的应用程序叫做 “MyApp”,你需要一个用来管理产品的控制器,按照常规就应该在app/controller/Products.js 文件中创建一个MyApp.controller.Products 的类.

Refs和Control(控制器构造函数中的两个参数)

Refs扩充了本已很强大的 ComponentQuery 语法,使之能够很容易的在网页上定位到组件,每个控制器都可以定义多个refs子项,例如这儿我们就定义了一个叫做“nav”的ref来引用该页面上id为“mainNav”的组件,在下面的addLogoutButton函数中我们会用到它。
Ext.define('MyApp.controller.Main', {
extend: 'Ext.app.Controller',
config: {
refs: { nav: '#mainNav' }
},
addLogoutButton: function() {
this.getNav().add({ text: 'Logout' });
}
});

通常一个ref就是一个键值对,键名(本例中是“nav”)就是这个即将生成的ref引用的名称,值(本例中是”#mainNav”)就是被用来查找组件的 ComponentQuery 选择器。

后面我们创建了一个名叫addLogoutButton的简单函数,它通过一个叫做“getNav”的方法使用了这个ref,这种以get开头的函数是根据你在refs中定义的ref名称按照指定格式自动生成的,即get后面紧跟(首字母)大写的ref名称。在这个例子当中我们把nav设定成一个Toolbar工具栏,而调用addLogoutButton会给nav工具栏添加一个Logout按钮,这个ref可以识别类似下面这样的Toolbar:
Ext.create('Ext.Toolbar', {
id: 'mainNav',
items: [

{ text: 'Some Button' }

]
});

假定这个toolbar已经被创建了(看代码,它里面已经有了一个button),我们调用'addLogoutButton'函数后,它会被加上第二个button。

Control

Refs的伴生配置属性是control(因为它们几乎总是配对出现),control是一种手段,通过它你可以侦听组件触发的事件,并且使你的控制器做出某种反应。control既可以接受ComponentQuery选择器又能接受refs名称作为它的键名,但是control的值必须是listener对象,比如下面:
Ext.define('MyApp.controller.Main', {
extend: 'Ext.app.Controller',
config: {
control: {
loginButton: { tap: 'doLogin' },
'button[action=logout]': { tap: 'doLogout' }
},
refs: { loginButton: 'button[action=login]' }
},
doLogin: function() { //called whenever the Login button is tapped },
doLogout: function() { //called whenever any Button with action=logout is tapped }
});
这里我们给出了两个control声明,一个针对名为loginButton的ref,另一个针对页面上所有action被设置为logout的按钮。我们给每一个声明都传入了一个事件处理程序,侦听到tap事件来自哪个按钮,哪个按钮指定的响应动作就会被执行。注意我们在control代码块中是使用字符串的方式来指定doLogin和doLogout函数的,这一点很重要。

每一个control声明中都可以按照你的需要侦听多个事件,并且允许混合使用ComponentQuery选择器和refs作为control的key。

Routes
路由

在ST2中,控制器可以直接指定哪个路由是它感兴趣的。这就使得我们可以在应用程序中提供访问历史支持,同样我们提供路由也可以实现深度链接,即任意链接至应用程序的任意部分。

假设说我们有一个负责响应登录和浏览用户资料的控制器,并且希望这些界面可以通过url直接访问到,我们可以这样做:
Ext.define('MyApp.controller.Users', {
extend: 'Ext.app.Controller',
config: {
routes: { 'login': 'showLogin', 'user/:id': 'showUserById' },
refs: { main: '#mainTabPanel' }
},
//uses our 'main' ref above to add a loginpanel to our main TabPanel (note that
//当添加loginpanel到主TabPanel的时候需要用到上面定义的叫做main的ref
//'loginpanel' is a custom xtype created for this application)
//注意’loginpanel’是我们为这个应用程序自定义的一个xtype类型
showLogin: function() {
this.getMain().add({ xtype: 'loginpanel' });
},
//Loads the User then adds a 'userprofile' view to the main TabPanel
//加载用户Model并添加一个userprofile视图到主TabPanel界面
showUserById: function(id) {
MyApp.model.User.load(id, {
scope: this,
success: function(user) {
this.getMain().add({ xtype: 'userprofile', user: user });
}
});
}
});

上面我们指定的routes可以很容易就把浏览器地址栏路径映射到一个与之匹配的控制器函数,routes可以是像“login”项那样的字符串,他匹配的路径是http://myapp.com/#login,也可以像'user/:id'那样包含通配符,他匹配的路径是类似http://myapp.com/#user/123 这种,当地址栏路径改变的时候,控制器就会自动调用指定的函数。

注意在showUIserById函数中,我们先加载了User Model的实例。当使用路由功能时,每个路由对应的函数都必须负责加载数据并还原状态,因为你的用户有可能把这个url发给其他人或者直接刷新了页面,这些情况下页面都会清空掉我们本已加载好的数据。关于如何在使用路由时还原状态,在应用程序架构指南里有更深入的探讨。

Before筛选器

Ext.define('MyApp.controller.Products', {
config: {
before: { editProduct: 'authenticate' },
routes: { 'product/edit/:id': 'editProduct' }
},
//this is not directly because our before filter is called first
//该函数不会直接运行因为before给它指定了一个需要优先执行的筛选器
editProduct: function() { //... performs the product editing logic // 在这里执行商品编辑逻辑 },
//this is run before editProduct
//该函数会在editProduct之前被调用
authenticate: function(action) {
MyApp.authenticate({
success: function() { action.resume(); },
failure: function() { Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in"); }
});
}
});


每当用户导航至一个这样的链接http://myapp.com/#product/edit/123,控制器的authenticate函数都会被首先调用并且被传入一个Ext.app.Action作为参数,该action其实就是在路由中指定的那个函数(如果没有before筛选器,这个函数才是应该被执行的)。这个action简单的描绘出了控制器、功能函数以及其他数据(比如url传过来的id)之间的关系。
筛选器能够以任意方式执行,同步异步均可。本例中我们使用了应用程序的authenticate方法去验证当前用户的身份。该方法将会触发一个AJAX请求来验证服务器上的用户凭证,因此它是使用同步方式执行的,验证成功后调用action.resume()将会把控制权交回给传入的action,失败则会告知用户他需要首先登陆。
Before筛选器同样可以用于在某些特定动作执行之前加载其他类文件,例如对于一些不常用的操作你可能会希望直到需用时才去加载(延迟按需加载)它的代码文件,这样应用程序可以启动更快些,为达到这个目的你可以设置一个筛选器中来使用Ext.Loader加载它们。

使用Before,你可以为每个action指定任意个数的筛选器,多个筛选器的时候只需要像这样传入一个数组:
Ext.define('MyApp.controller.Products', {
config: {
before: { editProduct: ['authenticate', 'ensureLoaded'] },
routes: { 'product/edit/:id': 'editProduct' }
},
//this is not directly because our before filter is called first
//该函数不会直接运行因为before给它指定了一个需要优先执行的筛选器
editProduct: function() { //... performs the product editing logic // 在这里执行商品编辑逻辑 } },
//this is the first filter that is called
//这是被调用的第一个筛选器函数
authenticate: function(action) {
MyApp.authenticate({
success: function() { action.resume(); },
failure: function() { Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in"); }
});
},
//this is the second filter that is called
//这是第二个要被调用的筛选器函数
ensureLoaded: function(action) {
Ext.require(['MyApp.custom.Class', 'MyApp.another.Class'], function() { action.resume(); });
}
});
筛选器函数会被依次调用,每一个当中都必须使用action.resume() 将程序控制权转交回来。

多设备配置下的控制器
超类,控制器的共享部分(该控制器将被不同设备的控制器继承)
Ext.define('MyApp.controller.Users', {
extend: 'Ext.app.Controller',
config: {
routes: { 'login': 'showLogin' },
refs: {
loginPanel: { selector: 'loginpanel', xtype: 'loginpanel', autoCreate: true }
},
control: {
'logoutbutton': { tap: 'logout' }
}
},
logout: function() { //code to close the user's session }
});

Phone Controller:
手机的控制器
Ext.define('MyApp.controller.phone.Users', {
extend: 'MypApp.controller.Users',
config: {
refs: { nav: '#mainNav' }
},
showLogin: function() { this.getNav().setActiveItem(this.getLoginPanel()); }
});

Tablet Controller:
平板电脑控制器
Ext.define('MyApp.controller.tablet.Users', {
extend: 'MyApp.controller.Users',
showLogin: function() { this.getLoginPanel().show(); }
});

到这,关于sencha touch 2基本知识介绍完了。
本想直接再讲一些例子,但发觉,例子真的没有那么重要。我应该说一些和手机相关的,如果做好了一个应用后,我们怎么弄呢,我想最重要是放在手机上,成为手机上应用。那么打包成为最需要了解的知识。

sencha touch 2 打包

如何把Sencha Touch 2.0框架打包成iOS本地应用
准备工作

软件

Mac OS X 10.6+
Xcode (iOS 模拟器所需要的)

Apple iOS provisioning

Complete iOS provisioning on the Apple iOS provisioning portal and have the certificates and devices setup through the provisioning portal and Xcode.
Create an App ID and finish provisioning your application. Please refer to the How-To section in the Apple iOS provisioning portal for help.

注意: 在打包iOS应用程序的过程中,需要用到App ID和App name。

在Mac系统上打包iOS系统应用程序的步骤

前期准备:进入 Apple iOS provisioning portal完成开发者授权。
安装打包程序部分: Sencha SDK Tools 2.0
创建一个配置文件用于打包IOS应用.
运行 packager 打包工具,创建一个App.

Step 1:在Apple iOS provisioning portal完成iOS程序开发者授权

可查看开发者授权系统Apple iOS provisioning portal 来获得开发和发布应用程序所需要的certificate和profile。

Step 2: 安装打包程序

运行Sencha SDK 安装程序: SenchaSDKTools (SenchaSDKTools-2.0.0-Beta)

包含有打包选项的的sencha command会被安装到指定的目录中(默认: Applications/SenchaSDKTools-2.0.0-Beta/command).

Step 3: 创建一个配置文件供打包iOS程序时使用.

配置文件具有如下的格式:

{ "applicationName": "<AppName>", "applicationId": "<AppID>", "outputPath": "<AppPackageOutputPath>", "iconName": "<AppIconName>", "versionString": "<AppVersion>", "webAppPath": "<PathToWebApp>", "configuration": "<Release | Debug>", "platform": "<iOSSimulator | iOS>", "deviceType": "<iPhone | iPad | Universal>", "certificateAlias": "<(Optional)CertificateAlias>", "orientations": [ "portrait", "landscapeLeft", "landscapeRight", "portraitUpsideDown" ] }

注意:在Mac系统Terminal上运行如下的命令行可以创建配置文件模板:

sencha package generate <configTemplate.json>

<configTemplate.json> 是配置文件的名字. 注意: <configTemplate.json> 的路径和文件名中不能包含空格。

下面参数用于iOS打包

"applicationName":"<AppName>"

AppName 以及AppID都可以在 iOS provisioning portal 中的App IDs部分中找到

AppID

例如上方所示:

AppName 为 “Sencha Touch 2 Packaging”
AppID 为 “com.Sencha.Touch2Packaging”

注意: App ID 需要与你在 Xcode的Identifier field输入的相同 。

你打包出文件的输出位置,<application.app>。

"iconName":"<AppIconName>"

你的应用程序所需要的图标文件。

注意: Retina图标需要在文件名的末尾用@2x标记。例如icon.png (普通的图标文件),icon@2x.png (retina图标文件). 如果带有 @2x.png的retina图标存在,打包程序将包含retina图标.
注意: 请参考 iOS icon guideline 来进一步了解图标文件类型。

"versionString":"<AppVersion>",

application的版本.

"webAppPath":"<PathToWebApp>"

用来打包的web application路径。

"configuration":"<Release | Debug>"

指定创建出程序的类型: Release or Debug.

"platform":"<Simulator | iOS>"

制定创建出程序的运行方式 iOS 模拟器还是iOS设备.

注意: the iOS simulator cannot run a signed build. A signed build can only be run on the device.

"deviceType":"<iPhone | iPad | Universal>"

指定设备类型.可选项: - iPhone:用于 iPhone 设备 – iPad:用于iPad 设备 – Universal:可用于iphone和iPad二者。

"certificateAlias":"<(Optional)CertificateAlias>"

这是一个非必选项. 你可以指定一个Certificate Alias来给你的app命名。

注意: 如果忽略此选项,默认的certificate将会是你在iOS Provisioning Portal中建立的那一个。

"orientations": [
"portrait",
"landscapeLeft",
"landscapeRight",
"portraitUpsideDown"
]

这是一个可选的配置项. 你可以给app指定方向. 可选项包括: “portrait”, “landscapeLeft”, “landscapeRight” and “portraitUpsideDown”

注意: 如果忽略此选项,默认将包括四个方向。

Step 4: 运行packager来打包获得一个程序

Sample debug configuration file

{ "applicationName":"Sencha Touch 2 Packaging", "applicationId":"com.sencha.touch2packaing", "iconName":"icon.png", "versionString":"1.0", "outputPath":"~/Desktop/STBuild-iOS", "webAppPath":"~/Desktop/www/", "configuration":"Debug", "platform":"iOSSimulator", "deviceType":"iPhone", "orientations": [ "portrait", "landscapeLeft", "landscapeRight", "portraitUpsideDown" ] }

Sample release configuration file

{ "applicationName":"Sencha Touch 2 Packaging", "applicationId":"com.sencha.touch2packaing", "iconName":"icon.png", "versionString":"1.0", "outputPath":"~/Desktop/STBuild-iOS", "webAppPath":"~/Desktop/www/", "configuration":"Release", "platform":"iOS", "deviceType":"iPhone", "orientations": [ "portrait", "landscapeLeft", "landscapeRight", "portraitUpsideDown" ] }

打包出一个在iOS 模拟器运行的app

准备工作: 在配置文件中设定Platform 和Configuration参数

例如:

"platform":"iOSSimulator"
"configuration":"Debug"

注意: 如果platform,configuration这两个选项没有提供 iOS将不能正常运行。

在终端运行以下命令,可以打包并在iOS上运行一个debug或unsigned的app。

sencha package run <configFile.json>

顺利执行完此命令之后,iOS模拟器将会运行app

注意: “deviceType” 选项将引导启动合适的模拟器: iPhone iPad.

打包出一个在 iOS 设备上运行的app

在终端运行以下命令,打包出一个在iOS设备上运行的程序

sencha package <configFile.json>

注意: <AppName.app> 将会输出在指定的位置,这是一个你可以在iOS设备上运行的程序。

原文链接: http://html5mobi.gotoip1.com/discussion/97/sencha-touch-2-sencha-touch-2-ios

以上打包方式是基于sencha 本身的。我觉得这样的方式不太好,也不方便,比较繁琐。
我推荐的方式是用phoneGap来打包。原因如下:
1.它为可以直接向其它iphone应用一样,在xcode下配置打包。
2.使用phoneGap后,可以调用本地设备
3.可以实现100%的Socket通讯,解决跨域问题

phoneGap:www.phonegap.com

提供几个包:
1.sencha touch 2 sdk 由于jira对上传文件大小做限制,需要的,可以直接问我要。或上网上下
2.phoneGap 1.8.1版本的包 由于jira对上传文件大小做限制,需要的,可以直接问我要。或上网上下
3.提供一个网上别人写的一个例子