跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

移动 web 应用程序框架比拼,第 1 部分: 用 SproutCore 构建移动应用程序

Michael Galpin, 软件架构师, eBay
Michael_Galpin 的照片
Galpin 是 eBay 的一名架构师,经常为 developerWorks 撰写文章。他曾在各类技术会议上发表演说,这些会议包括 JavaOne、EclipseCon 和 AjaxWorld 等。要了解 Michael 的工作进展,请您在 Twitter 上跟随 @michaelg 。

简介: Web 应用程序发展迅速,移动 web 应用程序的出现意味着再一次的飞跃。如今,人们期望 web 应用程序提供一个本地体验 — 一个和本地移动应用程序不差上下的移动 web 应用程序。尽管移动 web 浏览器提供了使之成为可能的功能,但相比本地应用程序开发,web 开发仍然处于初级阶段。SproutCore 是一个 web 应用程序框架,主要是为一个特定的设备开发类似于本地应用程序的 web 应用程序。探究 SproutCore 并将它作为一个构建移动 web 应用程序的框架来仔细研究。

发布日期: 2011 年 10 月 17 日
级别: 中级 原创语言: 英文
访问情况 : 2698 次浏览
评论: 


关于本系列

移动应用程序的开发发展迅速,许多开发人员选择走移动 web 路线,不再为每个不同的移动平台重复编写相同的应用程序 。然而,由于您 “追求 web”,您需要放弃本地移动应用程序开发人员曾经和容易构建的应用程序框架。结果是出现了几个 web 应用程序框架。在本系列的 4 篇文章中,我们将看到其中 4 个框架:SproutCore、Cappuccino、jQTouch 和 Sencha Touch。我们将对它们进行比较,评估使用它们构建一个移动 web 应用程序的优势和劣势。


先决条件

在本文中,我们将使用 SproutCore 框架创建一个简单的移动 web 应用程序。SproutCore 大量应用 Ruby 和 Ruby Gems 生成代码和构建系统。本文使用 Ruby 1.8.7 和 Gems 1.3.7。SproutCore 是一个纯 JavaScript 框架,没有服务器端组件和较小的 HTML 和 CSS。任何 web 服务器都可以。从 参考资料 获取这些工具的链接。


SproutCore 概述

您想要扩展您的 iOS 设备的开发实现非 iOS 用户吗? 移动 web 和 SproutCore 的结合可能就是您所需的框架。SproutCore 为 web 带来一个新的编程模型(灵感源于 Cocoa 框架)。

SproutCore 是 web 应用程序的第一个也是最重要的一个 Model-View-Controller (MVC) 框架。如果您是一个 web 开发人员,那么您可能对 MVC 框架,像 Struts 或 Ruby on Rails,比较熟悉。然而,SproutCore 不同于它们两个,因为它们是服务器端框架,而 SproutCore 是一个纯客户端框架,M、V 和 C 都是驻留在客户端的。这实际上是 MVC 运行一个更自然的方式;事实上,大多数桌面操作系统提供类似的 MVC 框架已经十几年了,因为这非常合适。

SproutCore 架构不仅仅是一个简单的 MVC 框架。它提供了一个绑定系统,不再需要大量粘合代码 来从模型获取数据或者在应用程序视图中使用它。这类代码在应用程序控制层是很常见的,但是在一个 SproutCore web 应用程序中这完全不需要。它也提供一个抽象的顶端数据存储和检索。SproutCore 提供一些相对轻量级的小部件,它们在移动应用程序中表现出色。SproutCore 的特性使得开发人员在一个较高级别的抽象上进行编程,而不是在 web 应用程序上;也不需要创建和访问 HTML 元素、管理 CSS 样式表或者使用 XMLHttpRequests 访问远程服务器。相反,除了 JavaScript 之外,您还可以使用类似于桌面开发或者本地移动应用程序开发的编程模型进行开发。


SproutCore 应用程序内部机制

一个 SproutCore 应用程序运行时通常只由一个 JavaScript 文件、一个 CSS 文件和一个 HTML 页面组成。但是,它以大量 JavaScript 文件开始,这些文件被编译成准备部署到任何静态 web 服务器上的优化文件。SproutCore 富有许多工具,包括构建工具。要让这些工具正常运行,它们就必须清楚您的源代码结构。SproutCore 信赖惯例优于配置( Convention over Configuration),包含生成项目结构的工具和将要部署的具体文件,比如模型和控制器。这些工具是使用 Ruby 构建的,功能非常类似于一些著名的 Ruby on Rails 框架工具。但是,SproutCore 是纯粹的 JavaScript。在您的服务器上不需要 Ruby。您只需要将 Ruby 安装在那些使用这些工具的开发机器上。

本文关注的是研究 SproutCore 应用程序如何工作,而不是如何使用这些工具。因此,一个合适的 SproutCore 教程是很有帮助的。要研究 SproutCore 应用程序,先来看一看您要使用 SproutCore 开发什么样的移动 web 应用程序。这个应用程序应该提供一个员工通讯录,它应该被设计为从移动设备访问。首先看看这个应用程序,特别是它的数据访问层。

数据、模型和存储

SproutCore 开发通常采用一个自底向上的方法,从描述应用程序所需的数据模型以及其如何从服务器访问开始。因为 Javascript 对象没有声明类型的信息,正式声明一个模型似乎有悖常理。但是,声明一个模型的优势是显而易见的。清单 1 展示了这个应用程序的数据模型。


清单 1. 员工数据模型
				
Intradir.Employee = SC.Record.extend({
    firstName: SC.Record.attr(String, { isRequired: YES }),
    lastName: SC.Record.attr(String, { isRequired: YES }),
    phone: SC.Record.attr(String),
    email: SC.Record.attr(String),
    fullTime: SC.Record.attr(Boolean, { defaultValue: YES }),
    fullName: function() {
        return this.getEach('firstName', 'lastName').compact().join(' ');
      }.property('firstName', 'lastName').cacheable()
}) ;

清单 1 中的代码显示了员工数据模型的定义。该应用程序称为 Intradir,对象在应用程序中有效。 SproutCore 模型称为记录,通常扩展 SproutCore 类 SC.Record。声明您记录的属性和类型。清单 1 有 5 个简单属性:firstNamelastNamephoneemailfullTime。前 4 个是字符串,最后一个是布尔值。要声明这个,使用 SC.Record.attr 帮助函数。这个帮助函数根据传递给它的类型(String、Boolean 和 Number)返回一个 SC.RecordAttribute 实例。您必须向帮助函数传递类型。您也可以传递可选属性。在清单 1 中,isRequired 选项触发 firstNamelastName 条件。fullTime 属性有一个默认值,被 defaultValue 选项触发。

最后,注意您已经声明了一个 fullName 属性,也为它提供了一个函数,这些计算属性在 SproutCore 中是非常重要的。在本例中,函数检索 firstNamelastName 属性,并将它们放入一个类似数组的结构中,然后使用连接函数将它们转换成一个字符串。这就是函数声明。在顶部,根据 firstNamelastName 属性声明该计算属性,且是可缓存的,这使得 SproutCore 在它第一次被计算之后缓存它的值,只要 firstNamelastName 值没发生变化就可以使用缓存值。这种优化为在移动设备上的 SproutCore 性能作出了贡献。

SproutCore 的 Record API 成为了轻量级对象关系映射(ORM)技术的基础。如果您有多种彼此相关的记录,您也可以定义一对多、多对一、和多对多的关系。要创建、存储或检索记录,您需要 SproutCore 的 Datastore API。SproutCore 为您的应用程序动态创建一个 Datastore。 需要做的就是为 Datastore 定义您的记录和数据源,如 清单 2 所示。


清单 2. 员工数据源
				
Intradir.EmployeesDataSource = SC.DataSource.extend({
  // ..........................................................
  // QUERY SUPPORT
  // 
  fetch: function(store, query) {
    if (query == Intradir.EMPLOYEE_QUERY_ALL){
        SC.Request.getUrl('/app/employees.json')
            .set('isJSON',YES)
            .notify(this, this._didFetchAllEmployees, {
                query: query,
                store: store
            }).send();
        return YES;
    }
    return NO;
  },
  _didFetchAllEmployees: function(response, params){
    if (SC.$ok(response)){
        store.loadRecords(Intradir.Employee, response.get('body'));
        store.dataSourceDidFetchQuery(query);
    } else {
        store.dataSourceDidErrorQuery(query, response);
    }
  },
  // ..........................................................
  // RECORD SUPPORT
  // 
  retrieveRecord: function(store, storeKey) {
    // Not supported
    return NO ; // return YES if you handled the storeKey
  },
  createRecord: function(store, storeKey) {
    // Not supported
    return NO ; // return YES if you handled the storeKey
  },
  updateRecord: function(store, storeKey) {
    // Not supported
    return NO ; // return YES if you handled the storeKey
  },
  destroyRecord: function(store, storeKey) {
    // Not supported
    return NO ; // return YES if you handled the storeKey
  }
}) ;

创建一个数据源最简单的方法是使用 SproutCore 代码生成工具。它为您提供一个对象,扩展 SC.DataSource 并为您提供 5 个函数来实现:fetchretrieveRecordcreateRecordupdateRecorddestroyRecord。在清单 2 的示例中,fetch 函数已经实现,无论执行任何查询,它都被调用;fetch 函数寻找一个特定查询。清单 3 显示该查询以及使用方法。


清单 3. Example of a SproutCore 查询示例
				
// Declare this in core.js so it can be used anywhere 
Intradir.EMPLOYEE_QUERY_ALL = SC.Query.local(Intradir.Employee);
Intradir = SC.Application.create({   
   NAMESPACE: 'Intradir',   
   VERSION: '0.1.0',   
// Create the store, and point it at our datasource   
store: SC.Store.create().from('Intradir.EmployeesDataSource') }) ;

// Now in your application you can use the query
var directory = Intradir.find(Intradir.EMPLOYEE_QUERY_ALL);

清单 3 中的样例代码显示的示例是 SproutCore 支持的查询中最简单一个。像这样的一个本地查询仅仅针对 Datastore 中本地存储的内容进行查询。然而,清单 2 显示的是 Datastore 从服务器加载的数据。为此,使用 SproutCore 中的 Ajax 实用工具。该调用是异步的,因为后台使用的是 XMLHttpRequest。因此,当数据从 服务器返回时,回调函数 _didFetchAllEmployees 被调用。您可以以任何形式调用此函数,但是必须遵守 SproutCore 的命名约定。带一个下划线前缀的函数表示它是私有的,使用 Cocoa 样式命名(didDoWhateverWeSaidWouldDo)。

如果您想支持其他查询,向 fetch 函数添加任何您需要的逻辑。这可以包含远程查询,其中查询结果直接从服务器得到,这通常涉及到自动形成一个 URL 来从服务器查询,通常通过添加回调函数来生成结果。类似地,SproutCore 数据源 API 支持对单个记录进行操作,尽管这在本例中不能实现。这通常取决于您后台的构架方式。对于这些在应用程序中的数据,您只需要显示它以及与它交互。在您进行这些操作之前,创建一个可以填入数据的用户界面。


JavaScript 视图、控制器和键值 observer

SproutCore 中的视图代码是用 JavaScript 写的,且使用一个声明式样式编写的,在很多方面和 HTML 相似。主要差别是 SproutCore 提供高级别的组件,您只需要将您的数据插入并连接到事件监听程序(通常称为 “observer”)。对于这个应用程序,加载一个简单的含有员工信息的表;该表支持在其任何列上分类。清单 4 显示了视图代码。


清单 4. 创建一个表视图
				
Intradir.nameColumn = SC.TableColumn.create({ 
    key: 'fullName', 
    label: 'Name', 
    width: 50 
});
Intradir.emailColumn = SC.TableColumn.create({ 
    key: 'email', 
    label: 'Email', 
    width: 50 
});
Intradir.phoneColumn =  SC.TableColumn.create({
    key: 'phone',
    label: 'Phone',
    width: 50
});
Intradir.mainPage = SC.Page.design({
    mainPane: SC.MainPane.design({
        childViews: 'tableView'.w(),
        tableView: SC.TableView.design({
            layout: { left: 15, right: 15, top: 15, bottom: 15 },
            backgroundColor: "white", 
            columns: [ 
                Intradir.nameColumn, 
                Intradir.emailColumn, 
                Intradir.phoneColumn
            ],
            contentBinding:   'Intradir.directoryController.arrangedObjects',
            selectionBinding: 'Intradir.directoryController.selection',
            selectOnMouseDown: YES,       
            exampleView: SC.TableRowView,        
            recordType: Intradir.Employee,
            nameColumn: Intradir.nameColumn,
            emailColumn: Intradir.emailColumn,
            phoneColumn: Intradir.phoneColumn
        })
    })
});

这段代码在 /resources/main_page.js 文件中可以找到。将这当作 JSON 数据,即使您也可以调用代码。减少视图脚本中使用的命令代码,注意绑定已经在创建的表和数据结构(是 directoryController 的一部分)之间声明了。返回此页的是控制器。清单 5 显示的是它的代码。


清单 5. 主页面的控制器
				
Intradir.directoryController = SC.ArrayController.create({
    // nothing to see here!
}) ;

清单 5 中显示的这个控制器是非常简单的。最重要的是它是一个数组控制器,这意味着它扩展了 SC.ArrayController。如果您返回到清单 4 中的绑定声明,引用的属性是 arrangedObjectsselection,在 SC.ArrayController 中定义的。这是一个常用的控制器,顾名思义,它有一个存储视图所需的模型数据的数组。这一过程所需的应用程序安装代码如 清单 6 所示。


清单 6. 应用程序安装代码
				
Intradir.main = function main() { 
    Intradir.getPath('mainPage.mainPane').append();
    var directory = Intradir.find(Intradir.EMPLOYEE_QUERY_ALL);
    var dirController = Intradir.directoryController;
    dirController.set('content', directory);
    var tableView = Intradir.getPath('mainPage.mainPane.tableView');
    
    // helper function
    function handleSort(key, column){
        var content = controller.get('content').sortProperty(key);
        if (column.get('sortState') === SC.SORT_DESCENDING){
            content = content.reverse();
        }
        dirController.set('content', content);
        tableView.set('content',content);
        tableView.displayDidChange();
        tableView.awake();
    }
    
    // add observers
    tableView.nameColumn.addObserver('sortState', function(){
        handleSort("fullName", tableView.nameColumn);
    });
    tableView.emailColumn.addObserver('sortState', function(){
        handleSort("email", tableView.emailColumn);
    });
    tableView.phoneColumn.addObserver('sortState', function(){
        handleSort("phone", tableView.phoneColumn);
    });     
};

function main() { Intradir.main(); }

使用 Datastore 中的数据填充控制器,这样控制器就有一个返回它的数组。如果您的目标只是显示表格,那就可以了,因为余下的代码可以完成分类。如果不是,定义一个帮助函数,您可以用来在表的每一列上进行分类。最后,分别向 3 列添加 observer。每个 observer 寻找一个 sortState 事件,然后使用帮助函数来对数据进行分类并刷新 UI。这种事件处理风格被称为 Key-Value Observing(KVO)。Cocoa 框架大量使用此范例;它被 SproutCore 引入 JavaScript 和 web 开发领域。


SproutCore 和移动开发

SproutCore 网站提出这样一个问题,“我们如何构建极其快速的桌面级(desktop-class)web 应用程序?”,并指出答案就是 SproutCore。 但是这个问题并没有提及移动 web 应用程序,只是强调了桌面级 web 应用程序。这是否意味着 SproutCore 是唯一一个只能从桌面 web 浏览器访问的 web 应用程序?要正确回答这一问题,考虑 Hedwig, 使用 SproutCore 的一个示例,专为触控设备设计的 web 应用程序。

Hedwig 示例向您既展示了移动应用程序使用 SproutCore 的好的一面,也展示了不好的一面。最好能在一个大尺寸触摸屏设备(比如 iPad)上看看。SproutCore 可以协助许多移动设备的基础开发,比如,处理触摸事件、方向变换和动态大小控制。如果您在一个较小的屏幕(iPhone 或者 Android 手机)上观察,那您将会注意到一些功能不能正常运行。 图 1 显示了 Hedwig 在 iPad 和在 iPhone 上的效果。


图 1. Hedwig 在 iPad 和在 iPhone 上的效果
Hedwig 在屏幕较大的 iPad 上看起来比屏幕较小的 iPhone  上要好一些

这里的问题主要是一个布局功能;页面是专为大屏幕设计的,并针对移动设备适当地安排一些屏幕外的控制。更进一步地说,它使用一个移动良好的特性 — 视窗,使页面可伸缩。这意味着当控件离开移动屏幕时,您不能缩小来访问控件。然而,为小触摸屏制作这样一个良好的页面只是举手之劳。

考虑使用 SproutCore 进行移动开发时,记住,目前它的许多 UI 组件并不是为小屏幕设计的。这些组件的大小或布局对于这些移动视频可能不是最好的。考虑组件的大小(就是说,需要多少 JavaScript 和 CSS)以及 CPU 和内存的密集程度。幸运的是,就显示速度而言,SproutCore 是最优的,因此移动设备中较慢的处理器不会对您造成太大的麻烦。但,使用更大、更复杂的组件(比如,示例中所用的 SC.TableView )时一定要格外小心。记住 SproutCore 含有帮助方法可以创建原始 HTML ,可通过一个定制的组件显示。


结束语

这篇介绍 SproutCore 的文章着重强调它的框架,以及如何使用它开发移动 web 应用程序。一方面,SproutCore 提供了一个丰富的客户端 MVC 框架,用来创建利用 JavaScript 作为编程语言的 web 应用程序。它使用绑定极大地减少了样板文件代码。它还提供了 Ajax 顶层抽象,并鼓励在客户端构建所有的 UI,进到服务器上访问数据。该构架对于使用 HTML5 技术(比如,应用程序缓存)的移动 web 应用程序是完美的。SproutCore 还包括对触摸事件的抽象,这可能导致在移动设备上的 UI 更为密集。另一方面,SproutCore 对于移动设备而言并不是最优的,并不是它的所有 UI 组件都适合移动设备,因此,您可能需要构建一些在智能手机上能运行得更好的定制 UI 组件。



下载

描述名字大小下载方法
Intradir 样例代码intradir.zip7KBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

关于作者

Michael_Galpin 的照片

Galpin 是 eBay 的一名架构师,经常为 developerWorks 撰写文章。他曾在各类技术会议上发表演说,这些会议包括 JavaOne、EclipseCon 和 AjaxWorld 等。要了解 Michael 的工作进展,请您在 Twitter 上跟随 @michaelg 。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development, Open source
ArticleID=765847
ArticleTitle=移动 web 应用程序框架比拼,第 1 部分: 用 SproutCore 构建移动应用程序
publish-date=10172011

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。