使用 Dojo Mobile 开发轻量级移动 Web 应用程序

优化和性能方面的考虑事项

Dojo Mobile 是基于 Dojo 的小部件集合,用于创建移动 Web 应用程序。使用 Dojo Mobile,您可以开发轻量级、高性能的移动 Web 应用程序。在本文中,将了解 Dojo Mobile 如何解决性能问题,以及如何优化基于 Dojo Mobile 的用户应用程序,使它们尽可能小而有效。

Yoshiroh Kamiyama, 软件工程师, IBM

Yoshiroh Kamiyama 的照片Yoshiroh Kamiyama 是一名咨询软件工程师,效力于 IBM 日本的 Yamato Software Lab (YSL)。他目前正在从事 WebSphere Feature Pack for Web 2.0 and Mobile 方面的工作,之前开发过几个项目,其中包括 Mobile Mashup、Lotus iNotes 和 Rational Portfolio Manager,大多数项目都使用了 Dojo Toolkit。他是 dojox.mobile 的原始贡献者,也是 Dojo Toolkit 的提交者。



2012 年 5 月 22 日

Dojo Mobile 简介

下载并试用 IBM Mobile Technology Preview

利用 IBM Mobile Technology Preview 开始构建移动应用程序,然后将该应用程序扩展并集成到企业中。预览版中的代码示例和服务包括 RESTful 通知服务、PhoneGap(构建混合移动应用程序的开源框架)、轻量级 WebSphere Application Server 运行时和示例代码,支持您了解它们如何工作。

现在市场上的大多数移动电话都有功能完善的 Web 浏览器,它们几乎与桌面浏览器没有什么差别。这些移动 Web 浏览器可处理大多数最新的 Web 技术,比如 HTML5、CSS3、JavaScript、DOM 操作和 Ajax。

Dojo Toolkit 已针对桌面 Web 应用程序开发进行了改进,而且 Dojo 可在移动 Web 浏览器上运行。既然 Dojo 已提供了许多有用的小部件,那么我们确实需要针对移动应用程序开发一些小部件吗?是的,有必要,原因如下:

  1. 输入设备的差异:大多数移动设备都没有鼠标或物理键盘。相反,它们提供了支持手指操作的触摸屏。桌面应用程序上的用户交互包括拖放应用程序、鼠标悬停操作、键盘快捷键和导航(使用 TAB 键或方向键导航)等,它们对移动设备提出了挑战。移动设备的任何 UI 小部件都应该支持触摸。
  2. 更小的屏幕:移动界面很小,用户在触摸屏上使用手指。恰当地设计移动设备的 UI 很重要。此外,平板设备的屏幕大小比智能手机的屏幕要大很多。移动 Web 应用程序需要根据屏幕大小或屏幕方向变换的用户界面。
  3. 性能问题:尽管移动设备的处理器能力在迅速增加,但是它们的计算能力仍然比台式机的弱。此外,移动网络带宽比台式机的带宽要低得多;有时带宽非常低。移动 Web 应用程序应该占用极小的内存并且性能很高。

获取 IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile

IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile 包括 IBM Dojo 1.7 Toolkit,这是一种新的移动和富 Internet 应用程序 (RIA) 构建块,也是一个基于 Dojo 的图形组件。使用随附提供的 Rational 工具,Feature Pack 帮助您利用最初针对桌面浏览器开发的 WebSphere 应用程序,改编它们并将它们部署到移动设备上。

Dojo Mobile 是一个基于 Dojo 的小部件集合,用于创建移动 Web 应用程序。它作为 dojox.mobile 引入到了 Dojo 1.5 中,用于解决上述问题。但是,输入设备和屏幕大小问题可使用传统方法解决,即通过添加移动支持来改进现有 Dojo 小部件,无需针对移动设备重新开发小部件。但是,很明显,性能问题无法使用这种方式解决。因此,Dojo Mobile 最重要(和具有挑战性)的一个任务是处理性能问题,尤其是最大程度地减少代码。


无图像的 UI 小部件

Dojo Mobile 的一个有趣功能是,它没有使用任何图像来构建 UI 小部件。基于 WebKit 的浏览器(移动设备上最流行的浏览器)支持 CSS3。CSS3 功能,比如渐变背景、圆角框和 DOM 元素的转换,可在无需使用图像的情况下处理图形表示形式。图 1 显示了 Dojo Mobile 提供的一些 UI 小部件。这些小部件没有使用图像;相反,它们使用了 DOM 元素,比如 <div> 使用 CSS 样式构建其 UI。使用许多图像会增加总下载大小和服务器的 HTTP 请求数量,从而导致性能下降。Dojo Mobile 通过使用 CSS3 功能尽可能避免了此问题。

图 1. 无图像的 Dojo Mobile 小部件
具有 UI 的无图像的 Dojo Mobile 小部件

另一方面,用户应用程序可使用图像。图 2 显示了作为 Dojo Mobile 测试文件提供的示例。如您所见,标签栏按钮、列表项图标和图标容器都是图像。滥用图像可导致性能下降,但这是应用程序自己的选择。Dojo Mobile 本身不使用图像,也不会强制用户应用程序使用图像。此外,如您将在后面看到的那样,Dojo Mobile 提供一些有用机制,比如 DOM button 和 CSS sprite,这可以降低图像需求并减少服务器的 HTTP 请求的数量。

图 2. 用户应用程序的图像
用户应用程序使用的图像示例

CSS Sprite

当使用图像是不可避免的事情时,有两种可能的方法可以最大程度地减少性能下降。一种方法是通过减少颜色数或增加压缩率来降低图像文件的大小。另一种方法是通过将多个小图像聚合到一个大图像中来减少图像的 HTTP 请求数量。可使用 CSS 属性裁剪和调整组合的大图像,以便它可以用作一个小图像。此技术称为 CSS Sprite,Dojo Mobile 支持这种方法。

清单 1 显示了列表项示例,每个列表项都使用单独的图标。图 3 显示了结果。此示例生成了图像的三个 HTTP 请求。

清单 1. 具有单独图像的列表项
<ul dojoType="dojox.mobile.RoundRectList">
  <li dojoType="dojox.mobile.ListItem" icon="i-icon-1.png" moveTo="bar">General</li>
  <li dojoType="dojox.mobile.ListItem" icon="i-icon-2.png" moveTo="bar">Photos</li>
  <li dojoType="dojox.mobile.ListItem" icon="i-icon-3.png" moveTo="bar">Store</li>
</ul>
图 3. 清单 1 的结果
具有单独图像的列表项的结果

另一方面,清单 2 显示了一个使用聚合图像 (i-icon-all.png) 的示例,这由列表项共享。通过指定 ListItem 小部件的 iconPos 属性的逗号分隔值(top、left、width 和 height),您可以重用聚合图像的任意矩形区域。如图 4 所示,结果是一样的,但它仅为聚合图像生成了一个 HTTP 请求。

清单 2. 具有聚合图像的列表项
<ul dojoType="dojox.mobile.RoundRectList" iconBase="i-icon-all.png">
  <li dojoType="dojox.mobile.ListItem" iconPos="0,0,29,29" moveTo="bar">General</li>
  <li dojoType="dojox.mobile.ListItem" iconPos="0,29,29,29" moveTo="bar">Photos</li>
  <li dojoType="dojox.mobile.ListItem" iconPos="0,58,29,29" moveTo="bar">Store</li>
</ul>
图 4. 清单 2 的结果
具有聚合图像的列表项的结果

CSS Sprite 还可用于图标容器的图标(参见 图 5)或标签栏的按钮(参见 图 6)。

图 5. 使用 CSS Sprite 的 IconContainer
使用 CSS sprite 的图标容器
图 6. 使用 CSS Sprite 的 TabBar
使用 CSS sprite 的标签栏

DOM Button

Dojo Mobile 为列表项提供简单的按钮、图标和复选标记/箭头等,如图 7 所示。它们通过将没有图像的 CSS 样式应用于 <div> 元素而获得,称为 DOM Button。

图 7. DOM Button
DOM button

理想情况下,DOM Button 的字节较小,因为它们来自不带图像的 CSS。表 1 比较了 DOM Button 和使用 DomButtonGreenCircleArrow (图 7 中的第四行第一列)作为示例的图像的字节大小。您可以看到 DOM Button 比图像小很多。

表 1. DOM Button 和图像的字节大小比较
文件字节大小
DomButtonGreenCircleArrow.css (minified & gzipped)392 字节
mblDomButtonGreenCircleArrow.png1,599 字节

DOM Button 还有其他好处,即不需要进行其他 HTTP 请求,因为可以使用构建工具将它们集成到主题 CSS 文件中。在这种情况下,DOM Button 占用的内存要小很多。表 2 显示了一个聚合 CSS 文件中 DOM Button 的字节大小。您可以看到添加 DomButtonGreenCircleArrow.css 仅增加了 155 个字节。

表 2. 聚合 CSS 文件中 DOM Button 的字节大小
文件字节大小
base.css (minified & gzipped)2,844 字节
base.css + DomButtonGreenCircleArrow.css (minified & gzipped)2,999 字节
Difference155 字节

DOM Button 结构

DOM Button 由应用了 CSS 样式的多个嵌套的 <div> 元素组成。如前所述,您可以通过使用 CSS3 的丰富功能来实现各种图形表示,比如渐变背景、圆角框和 DOM 元素的转换。您甚至可以通过调整正方形 <div> 元素的边界半径来绘制圆形。图 8 显示了一个 DOM Button 结构的示例。

图 8. DOM Button 的结构
DOM button 的结构

如何使用 DOM Button

DOM Button 只是一个 CSS 样式集。它不需要 JavaScript 代码。只需放置几个嵌套的 <div> 元素并为它们应用 CSS 样式,就可以显示它们。清单 3 显示了最小示例代码,图 9 显示了结果。

清单 3. 在不使用 JavaScript 的情况下使用 DOM Button
<html>
  <head>
    <link href="themes/common/domButtons/DomButtonBlueCircleMinus.css" rel="stylesheet">
  </head>
  <body>
    <div class="mblDomButtonBlueCircleMinus">
      <div><div><div></div></div></div>
    </div>
  </body>
</html>
图 9. 清单 3 的结果
清单 3 的结果

在 清单 3 中,我准备了 4 个级别的嵌套式 DIV,其中包括 DOM Button 的 root 节点,此 root 节点是一个类名称。但是,所需准备的 DIV 的数量具体取决于 DOM Button 的类型。要自动创建必要数量的 DIV,请使用 Dojo Mobile 的 dojox.mobile.createDomButton() 功能,如 清单 4 所示。

清单 4. 在使用 JavaScript 的情况下使用 DOM Button
<div id="btn1" class="mblDomButtonBlueCircleMinus"></div>
----
var node = dojo.byId("btn1");
dojox.mobile.createDomButton(node);

但是,通常不使用 清单 3 和 清单 4。我之所以提供它们是为了展示 DOM Button 的工作原理。相反,一些 Dojo Mobile 小部件支持如下所示的 DOM Button。您可以在图像图标或按钮的位置指定 DOM Button。

  • ListItem:icon、rightIcon、rightIcon2、arrowClass 和 checkClass 属性(参见 图 10)
  • TabBarButton:icon1 和 icon2 属性(参见 图 11)
  • ToolBarButton:icon 属性(参见 图 12)
图 10. ListItem(列表)
列表项
图 11. TabBarButton(标签栏)
标签栏按钮
图 12. ToolBarButton(标题)
工具栏按钮

如何创建自定义 DOM Button

您可以创建自己的自定义 DOM Button。下面是一些提示。

  • DOM Button 的类名称必须以 "mblDomButton" 开始。Dojo Mobile 检查它来确定按钮是否为 DOM Button 。(另一方面,CSS 文件名和放置位置由您决定。)
  • 指定 DOM Button 到其根节点的高度和宽度。它们将是整个 DOM Button 的大小。
  • <div> 元素是嵌套的,不是同级的。因此,您可能需要对 父节点下的节点应用 CSS 样式。
  • 要为 <div> 层次结构中的具体 <div> 元素应用 CSS 样式,可以使用子选择符 (>)。
  • 如果您想要支持非 CSS3 桌面浏览器,则应该创建一个等效图像和一个 compat css 文件 (*-compat.css)。

清单 5 和 清单 6 显示了一个自定义 DOM Button 示例。图 13 显示了结果。

清单 5. 自定义 DOM Button (DomButtonMyCheck.css)
/* === Red Check Button ==*/
.mblDomButtonMyCheck {
  position: relative;
  width: 29px;
  height: 29px;
}
.mblDomButtonMyCheck > div {
  position: absolute;
  left: 0px;
  top: 8px;
  width: 16px;
  height: 6px;
  font-size: 1px;
  -webkit-transform: scaleX(0.7) rotate(135deg);
  border-width: 3px 4px 0px 0px;
  border-style: solid;
  border-color: red;
}
清单 6. 自定义 DOM Button (DomButtonMyCheck-compat.css)
/* === Red Check Button ==*/
.mblDomButtonMyCheck {
	background-image: url(compat/mblDomButtonMyCheck.png);
	background-repeat: no-repeat;
}
.mblDomButtonMyCheck > div {
	display: none;
}
图 13. 自定义 DOM Button
自定义 DOM button

兼容性模块

Dojo Mobile 针对基于 WebKit 的移动浏览器进行了优化,但它也支持非 WebKit 桌面浏览器。但是,没有人希望牺牲移动设备上的性能来支持桌面浏览器。当应用程序在移动设备上运行时,不应加载跨浏览器支持代码。

条件编译

一种方法是条件编译,这种方法支持您删除移动设备不需要的一些代码块。Dojo 提供了两种条件编译方法 pragmas 和 has()

Pragmas 是可用于包含/排除一些代码部分的特殊指令,如 清单 7 所示。

清单 7. pragma 使用示例
process: function(){
//>>excludeStart("marker1", kwArgs.noIE);
    if(dojo.isIE){
        ....
    }
//>>excludeEnd("marker1");
    ....
}

在上述示例中,如果您使用如下所示的 noIE=true 条件选项构建代码,则会从构建中删除 excludeStartexcludeEnd 之间的代码块。

> build profile=myapp action=release noIE=true

此外,如果您使用 includeStart/includeEnd pragmas,则只有在条件匹配时才会包含包围的代码块。

has()(或者 dojo/hashas.js)是一个功能检测/测试机制,包含在 Dojo 1.7 中。如清单 8 所示,您可以使用它来测试当前的运行时环境中是否存在某个特定功能。

清单 8. "has" 测试的示例
process: function(){
    if(has("ie")){
        ....
    }
    ....
}

如果您在构建版本中配置 staticHasFeatures 数组,如下所示,会使用 0 代替 has("ie"),然后编译器的优化器将其代码块视为无用代码,并从构建中删除该代码块。

staticHasFeatures: {
  'ie': 0
}

兼容性模块

条件编译支持您针对具体平台进行优化的构建。但是,该构建的问题是它不能在其他平台上工作。要支持其他平台,您需要进行其他构建。要解决此问题,Dojo Mobile 采用了一种不同的方法:兼容性模块(或 compat)。

Compat 包含一些支持非 Webkit 浏览器的代码。如果加载了 compat,它会 "重写" 一些需要修正的小部件方法。 重写代码会保持原始小部件的类名称,不会影响用户的应用程序代码。另一方面,通过将原始小部件划分为多个子类来重写代码将要求新定义的小部件使用新类名称。 Compat 还会加载 *-compat.css 文件,这些文件是原始 css 文件的补丁。在 compat 模式下,传统方法用于在不使用 CSS3 的情况下呈现小部件 UI,比如使用 dojo.fx 的基于 JavaScript 的动画和背景图像等。要为您的应用程序使用 compat,请使用 dojo.require()require API 加载 "dojox.mobile.compat"。

注意,compat 被划分为两部分:compat.js_compat.js。 _compat.js 包含所有实现代码,而 compat.js 是 _compat.js 的加载程序。compat.js 只在当前浏览器是基于 Webkit 的浏览器时才会加载 _compat.js。compat.js 只是一个小代码块,即使它在构建中也不会影响移动性能。这样,在基于 Webkit 的浏览器上,因为永远不会加载 _compat.js ,所以性能不会下降。在非 WebKit 浏览器上,会自动加载 _compat.js,并且 Dojo Mobile 可以在 compat 模式下工作。表 3 显示了具有 compat 的移动版本和没有 compat 的移动版本之间的字节大小比较。如您所见,差别小到可以忽略不计:45 字节。

表 3. 移动版本大小比较(配置文件:mobile-all.profile.js、minified 和 gzipped)
描述字节大小
没有 compat 的移动版本36,594 字节
具有 compat 的移动版本36,639 字节
差别45 字节

性能评估提示

如前所述,在非 WebKit 浏览器上,Dojo Mobile 在 compat 模式下运行,并且会加载其他资源,比如图像和/或 *-compat.css。因此,如果您想要评估在移动设备中加载的文件和文件数量,则应该使用非 WebKit 浏览器,比如 Firefox 或 Internet Explorer。Google Chrome 的调试程序和桌面版 Safari 是监控网络流量的好工具,因为它们都基于 WebKit。


模块依赖关系

Dojo 提供了很多有用的 API,并且将它们打包为模块,模块是相对小的 JavaScript 文件。有无数的有用模块可用。但是,滥用它们可能会占用大量内存。Dojo Mobile 被精心设计为仅包含少量模块依赖关系。例如,它故意没有使用一些常用的有用模块,比如 dojo.query 或 dijit._Templated。此外,所有 Dojo Mobile 小部件都继承自 dijit._WidgetBase 而不是 dijit._Widget。dijit._WidgetBase 是 dijit._Widget 的基类,不包含移动设备不需要的一些代码,比如键盘支持代码,因此它比 dijit._Widget 更轻。此外,它也不使用 dijit._base.manager,这是 Dojo 1.6 之前的必需模块;相反,它使用 dijit.registry,这是 dijit._base.manager 的子集。

使用像 dojo.query 这样的 dojo 模块还是来自基于 Dojo Mobile 的用户应用程序的 dijit._Templated 当然是您自己的选择。 但是,您应该根据性能慎重地选择使用哪个 dojo/dijit 模块。例如,如果您在应用程序中仅使用了一次 dojo.query,则可使用比整个 dojo.query 实现小很多的代码来代替它。

清单 9 显示了 Dojo Mobile 基类直接或间接相关的模块。您可能注意到,与典型的桌面用例相比,它具有较少的 dojo/dijit 模块依赖关系。它仅与 25 个模块中的 11 个 dojo/_base 模块相关,与 dijit/_base 模块没有任何相关性。如果您使用未在清单 9 中显示的其他模块,则会增加应用程序的内存占用。当您使用此类模块时,应该慎重地选择它们。

清单 9. 依赖关系模块
dijit/_Contained
dijit/_Container
dijit/_WidgetBase
dijit/main
dijit/registry
dojo/Evented
dojo/Stateful
dojo/_base/Deferred
dojo/_base/array
dojo/_base/config
dojo/_base/connect
dojo/_base/declare
dojo/_base/event
dojo/_base/kernel
dojo/_base/lang
dojo/_base/sniff
dojo/_base/unload
dojo/_base/window
dojo/aspect
dojo/dom
dojo/dom-attr
dojo/dom-class
dojo/dom-construct
dojo/dom-geometry
dojo/dom-prop
dojo/dom-style
dojo/domReady
dojo/has
dojo/keys
dojo/mouse
dojo/on
dojo/ready
dojo/topic

构建

要优化加载时间性能,构建是一个重要的过程。Dojo 有一个强大的构建系统,会将所有 JavaScript 和 CSS 压缩到较少的文件,并创建一个高效的应用程序版本以供发布。图 15 和图 16 显示了非构建(源代码)应用程序和构建应用程序之间的传输文件比较。您可以看到,它们在请求数量和总体传输大小方面有很大差别。下一部分将讨论构建 Dojo Mobile。

图 14. 请求数量和下载大小(非构建)
请求数量和下载大小(非构建)
图 15. 请求数量和下载大小(构建)
请求数量和下载大小(构建)

customBase 构建

Dojo 构建有一个名为 customBase 的选项。标准 Dojo 构建包含构建中的大多数 Dojo 基础模块,因为 Dojo 基础模块总是可用,所以无需明确要求它们。但是,如果您制作了一个启用了 customBase 选项的构建,则只有实际依赖关系链中的模块包含在构建中。Dojo Mobile 被设计为包含尽量少的 Dojo 基础模块依赖关系。 清单 10 显示了在构建配置文件中如何指定 customBase 选项。

清单 10. 配置文件中的 customBase 选项
dependencies = {
  stripConsole: "normal",

  layers: [
    {
      name: "dojo.js",
      customBase: true,
      dependencies: [
        "dojox.mobile.parser",
        "dojox.mobile",
        "dojox.mobile.compat"
      ]
    },
  ....

Closure compiler

可使用两种编译器(或压缩器)生成 Dojo 构建工具,即 Shrinksafe 和 Google Closure Compiler。Shrinksafe 是 Dojo 的默认编译器。从版本 1.7 开始 Closure 就与 Dojo 捆绑在一起。表 4 显示了 Shrinksafe 和 Closure 之间的移动构建大小比较。如您所见,Closure 的压缩率比 Shrinksafe 好,并且 Closure 构建的大小也比 Shrinksafe 构建小 20%,至少在本例中是这样的。

表 4. Shrinksafe 和 Closure 之间的移动构建大小比较
描述字节大小
移动构建 (Shrinksafe, gzipped)44,826 字节
移动构建 (Closure, gzipped)36,639 字节

Dojo Mobile 已经根据使用 Closure 构建的内容进行了测试。建议对移动构建使用 Closure,因为其输出较小。

如何构建 Dojo Mobile

Dojo Mobile 提供了两个示例配置文件:mobile-all.profile.js 和 mobile.profile.js,这两个文件都位于 dojo/util/buildscripts/profiles 文件夹。要使用这些文件轻松进行构建,请使用 dojox/mobile/build 文件夹中可用的简单的批处理文件。

从命令行,您可用运行批处理文件。对于 Windows 请使用 build.bat ,对于 Linux 请使用 build.sh

清单 11. 使用构建批处理文件
  > build
  Usage: build separate|single [webkit]
    separate  Create mobile.js that includes only dojox.mobile
    single    Create a single dojo.js layer that includes dojox.mobile
    webkit    Enable webkitMobile=true option (Loses PC browser support)

separate 使用 mobile.profile.js 并创建仅包含 dojox.mobile 基础模块的 mobile.js。它不包含 dojo 基础模块或 dijit 基础模块。还会为桌面浏览器支持创建 _compat.js。此外,还创建了 dojo.js,但它只是一个普通的 dojo 基础构建,不是 customBase 构建。

注意,"the dojox.mobile base" 不包含所有 Dojo Mobile 小部件。例如,ScrollableView、Carousel、SpinWheel 和表单控件等不包含在此 base 中。如果您想要在构建中使用它们,可将它们添加到配置文件中的依赖关系数组中。清单 12 显示了所有部署到 dojox.mobile 基础构建 mobile.js 中的模块。

清单 12. mobile.js 中的模块
dojox/main
dojox/mobile
dojox/mobile/EdgeToEdgeCategory
dojox/mobile/EdgeToEdgeList
dojox/mobile/Heading
dojox/mobile/ListItem
dojox/mobile/ProgressIndicator
dojox/mobile/RoundRect
dojox/mobile/RoundRectCategory
dojox/mobile/RoundRectList
dojox/mobile/Switch
dojox/mobile/ToolBarButton
dojox/mobile/TransitionEvent
dojox/mobile/View
dojox/mobile/ViewController
dojox/mobile/_ItemBase
dojox/mobile/_base
dojox/mobile/common
dojox/mobile/compat
dojox/mobile/sniff
dojox/mobile/transition
dojox/mobile/uacss

single 使用 mobile-all.profile.js 并包含一个 dojo.js 层,该层包括 dojox.mobile 基础模块和所有相关 dojo/dijit 模块。还为桌面浏览器支持创建了 _compat.js。此构建支持 customBase 选项。因此,在生成的 dojo.js 中仅包含最少的 dojo/dijit 基础模块。清单 13 显示了部署到单个构建中的所有模块。

清单 13. dojo.js 中的模块
dijit/_Contained
dijit/_Container
dijit/_WidgetBase
dijit/main
dijit/registry
dojo/Evented
dojo/Stateful
dojo/_base/Deferred
dojo/_base/array
dojo/_base/config
dojo/_base/connect
dojo/_base/declare
dojo/_base/event
dojo/_base/kernel
dojo/_base/lang
dojo/_base/sniff
dojo/_base/unload
dojo/_base/window
dojo/aspect
dojo/dom
dojo/dom-attr
dojo/dom-class
dojo/dom-construct
dojo/dom-geometry
dojo/dom-prop
dojo/dom-style
dojo/domReady
dojo/has
dojo/keys
dojo/mouse
dojo/on
dojo/ready
dojo/topic
dojox/main
dojox/mobile
dojox/mobile/EdgeToEdgeCategory
dojox/mobile/EdgeToEdgeList
dojox/mobile/Heading
dojox/mobile/ListItem
dojox/mobile/ProgressIndicator
dojox/mobile/RoundRect
dojox/mobile/RoundRectCategory
dojox/mobile/RoundRectList
dojox/mobile/Switch
dojox/mobile/ToolBarButton
dojox/mobile/TransitionEvent
dojox/mobile/View
dojox/mobile/ViewController
dojox/mobile/_ItemBase
dojox/mobile/_base
dojox/mobile/common
dojox/mobile/compat
dojox/mobile/parser
dojox/mobile/sniff
dojox/mobile/transition
dojox/mobile/uacss

webkit 支持 webkitMobile=true 构建选项,这会将基于 Webkit 的移动浏览器不需要的代码块删除。例如,会从构建中删除特定于 Internet Explorer 或 Firefox 的代码。这会减少总体代码大小,但是构建模块不会在桌面浏览器上工作,即使这些浏览器有兼容性模块 (compat.js)。

还要注意,此选项明显减少了 Dojo 1.6 中的代码量,但在 Dojo 1.7 中,功能检测/测试转移到了 dojo.has,因此 webkitMobile 选项对代码减少不再有明显影响。


CSS 聚合

Dojo Mobile 在 themes 文件夹下有很多 CSS 文件。每个小部件都有一个 CSS 文件,并且它们可供每个主题(Android、Blackberry、Custom 和 iPhone)使用。此外,还有针对 DOM Buttons 或过渡动画的 CSS 文件。要帮助您轻松地在应用程序中包含必需的 CSS 文件,一些入口点 CSS 文件中汇集了许多相关的 CSS 文件:

  • 所有主题文件(示例:themes/iphone/iphone.css)
    包含所有常见 CSS 文件和主题 CSS 文件
  • 基本主题文件(示例:themes/iphone/base.css)
    包含 dojox.mobile 基本模块的主题 CSS 文件
  • DOM Buttons (themes/common/domButtons.css)
    包含所有 DOM Buttons
  • 过渡 (themes/common/transitions.css)
    包含所有过渡动画

使用所有主题文件是最简单的,因为它包含一切文件。但是,这意味着可能包含无用的 CSS 文件,而这可能会增加不必要的内存占用。在这种情况下,一种方法是使用基本主题文件和未包含在基本主题文件中的单个 CSS 文件。

上述入口点 CSS 文件是 @import 指令的集合。如果您执行 Dojo 构建,会内嵌 @import 语句,并会在入口点 CSS 文件中纳入引用的 CSS 文件。因此,使用一个入口点 CSS 文件仅生成一个 HTTP 请求,如果构建了该文件。但是,如果使用 <link> 或 @import 在应用程序中列出了 CSS 文件,如清单 14 所示,将会为每个文件生成一个 HTTP 请求。

清单 14. html 中的链接标签 (userApp.html)
<head>
  <link href="../themes/iphone/base.css" rel="stylesheet">
  <link href="../themes/iphone/TabBar.css" rel="stylesheet">
  <link href="../themes/common/SpinWheel.css" rel="stylesheet">
  <link href="../themes/common/domButtons/DomButtonBlueCircleArrow.css" rel="stylesheet">
  ....

要生成所需的请求数量,请使用 @import 指令创建一个入口点 CSS 文件(包含对最少必须 CSS 文件的引用),如清单 15 所示。如果您执行 Dojo 构建,将生成没有外部引用的内联 CSS 文件。您可以从应用程序使用它,如清单 16 所示。

清单 15. 聚合的 CSS 文件 (myStyles.css)
@import url("../themes/iphone/base.css");
@import url("../themes/iphone/TabBar.css");
@import url("../themes/common/SpinWheel.css");
@import url("../themes/common/domButtons/DomButtonBlueCircleArrow.css");
清单 16. html 中的一个链接标签 (userApp.html)
<link href="myStyles.css" rel="stylesheet">

如前所述,您可以通过创建自己的入口点 CSS 文件并执行 Dojo 构建来减少 CSS 文件的 HTTP 请求数量。


动态创建和延迟加载

延迟加载(一种只在第一次引用模块时才会加载它们的技术)改进了应用程序的启动性能。Dojo Mobile 本身内部使用的就是延迟加载技术。

动态地创建视图

使用 _ItemBaseurl 属性,您可以在进行视图转换之前立刻动态地创建一个新视图。清单 17 显示了一个使用 url 属性的示例。视图内容可以是 HTML 片段(清单 18)或 JSON 数据(清单 19)。

清单 17. url 属性的示例
<ul dojoType="dojox.mobile.RoundRectList">
  <li dojoType="dojox.mobile.ListItem" url="view1.html">
    External View #1 (sync)
  </li>
  <li dojoType="dojox.mobile.ListItem" url="view2.json" sync="false">
    External View #2 (async)
  </li>
</ul>
清单 18. 动态视图的 HTML 片段 (view1.html)
<div dojoType="dojox.mobile.View">
  <h1 dojoType="dojox.mobile.Heading" back="Home" moveTo="foo">view1.html</h1>
  <ul dojoType="dojox.mobile.EdgeToEdgeList">
    <li dojoType="dojox.mobile.ListItem">
      Jack Coleman
    </li>
    <li dojoType="dojox.mobile.ListItem">
      James Evans
    </li>
    <li dojoType="dojox.mobile.ListItem">
      Jason Griffin
    </li>
  </ul>
</div>
清单 19. 动态视图的 JSON 数据 (view2.json)
{
  "dojox.mobile.View": {
    "dojox.mobile.Heading": {
      "@back": "Home",
      "@moveTo": "foo",
      "@label": "view1.json"
    },
    "dojox.mobile.EdgeToEdgeList": {
      "dojox.mobile.ListItem": [{
        "@label": "Jack Coleman"
      }, {
        "@label": "James Evans"
      }, {
        "@label": "Jason Griffin"
      }]
    }
  }
}

另一个选项是以编程方式获取视图内容并将它插入目标视图。清单 20 显示了一个示例。

清单 20. 将内容加载到现有视图并进行转换的示例
<li dojoType="dojox.mobile.ListItem" moveTo="#" onclick="myAction2(this)">
  Load and Move (async)
</li>
----
function myAction2(li){
   var view2 = dijit.byId("view2"); // destination view
   var listItem = dijit.byNode(li);
   var prog = dojox.mobile.ProgressIndicator.getInstance();
   dojo.body().appendChild(prog.domNode);
   prog.start();
   view2.destroyDescendants();

   var url = "http://..."; // or var url = listItem.url;
   dojo.xhrGet({
       url: url,
       handleAs: "text",
       load: function(response, ioArgs){
           var container = view2.containerNode;
           container.innerHTML = response;
           dojo.parser.parse(container);
           prog.stop();
           listItem.transitionTo("view2");
       }
   });
}

请参见 dojox/mobile/tests/test_list-actions.html 了解详细信息和更多示例。

使用 IconContainer 进行延迟加载

IconContainer 可以拥有多个图标,每个图标都表示一个子应用程序。子应用程序可由一个或多个 Dojo 小部件组成。在启动时加载所有子应用程序代码可能会增加主应用程序的启动时间。要改进启动时间性能,您可以指定 IconItems 上的 lazy="true" 参数。然后,Dojo 解析器会跳过 IconItem 内部的小部件实例化,并且在第一次打开图标时,IconContainer 会动态地加载这些图标内容模块并实例化它们。

清单 21 是一个图标内容的延迟加载示例。注意,您无需明确要求图标内容模块(下列示例中的 dijit.CalendarLite)。还要注意,在 Dojo 1.7 中,只有在同步加载程序的模式下才支持延迟加载,因为使用 dojo.require 可同步执行延迟加载。

清单 21. IconContainer 的延迟加载示例
<ul dojoType="dojox.mobile.IconContainer">
  <li dojoType="dojox.mobile.IconItem" label="Calendar" lazy="true">
    <div id="cal" dojoType="dijit.CalendarLite"></div>
  </li>
  ....
图 16. IconContainer
图标容器

使用 TabBar 进行延迟加载

如果您需要可延迟加载的标签面板,可使用 TabBar 小部件和在 "动态地创建视图" 中介绍的技术。对于标签面板,对第一个标签面板还有一些技巧,因为在启动时没有视图转换。您需要在启动时以编程方式进行从空白视图到第一个标签面板的视图转换,以将外部内容加载到第一个标签面板。清单 22 和清单 23 显示了相同的示例;前一个是同步示例,后一个是异步示例。

清单 22. 延迟标签面板的示例(同步模式)
  <script src="../../../dojo/dojo.js" djConfig="parseOnLoad: true"></script>
  <script language="JavaScript" type="text/javascript">
    dojo.require("dojox.mobile");
    dojo.require("dojox.mobile.parser");
    dojo.require("dojox.mobile.compat");
    dojo.require("dojox.mobile.TabBar");
    dojo.ready(function(){
      setTimeout(function(){
        dijit.byId("tab1").onClick();
      }, 0);
    });
  </script>
</head>
<body style="visibility:hidden;">
  <ul dojoType="dojox.mobile.TabBar" barType="segmentedControl">
    <li id="tab1" dojoType="dojox.mobile.TabBarButton" url="view1.html">New</li>
    <li id="tab2" dojoType="dojox.mobile.TabBarButton" url="view2.html">What's Hot</li>
    <li id="tab3" dojoType="dojox.mobile.TabBarButton" url="view3.html">Genius</li>
  </ul>

  <div id="blank" dojoType="dojox.mobile.View" selected="true"></div>
  <div id="view1" dojoType="dojox.mobile.View"></div>
  <div id="view2" dojoType="dojox.mobile.View"></div>
  <div id="view3" dojoType="dojox.mobile.View"></div>
</body>
清单 23. 延迟标签面板的示例(异步模式)
  <script src="../../../dojo/dojo.js" djConfig="async: true, parseOnLoad: true"></script>
  <script language="JavaScript" type="text/javascript">
  require([
    "dojo/_base/kernel",
    "dijit/registry",
    "dojox/mobile",
    "dojox/mobile/compat",
    "dojox/mobile/parser",
    "dojox/mobile/TabBar"
  ], function(dojo, registry){
    dojo.ready(function(){
      setTimeout(function(){
        registry.byId("tab1").onClick();
      }, 0);
    });
  });
  </script>
</head>
<body style="visibility:hidden;">
  <ul dojoType="dojox.mobile.TabBar" barType="segmentedControl">
    <li id="tab1" dojoType="dojox.mobile.TabBarButton"
        url="view1.html" sync="false">New</li>
    <li id="tab2" dojoType="dojox.mobile.TabBarButton"
        url="view2.html" sync="false">What's Hot</li>
    <li id="tab3" dojoType="dojox.mobile.TabBarButton"
        url="view3.html" sync="false">Genius</li>
  </ul>

  <div id="blank" dojoType="dojox.mobile.View" selected="true"></div>
  <div id="view1" dojoType="dojox.mobile.View"></div>
  <div id="view2" dojoType="dojox.mobile.View"></div>
  <div id="view3" dojoType="dojox.mobile.View"></div>
</body>

动态地要求模块

在运行时动态地要求模块是个好主意,因为这可以改进启动时间性能。但是要注意一个潜在问题。看看清单 24,它显示了以编程方式加载 Dojo 模块。它可以工作,但是如果您构建清单 24,构建工具将自动选择所需模块,并将它部署到构建中,因此在运行时 dojo.require 不执行任何操作,因为已在启动时加载了该模块。

清单 24. 以编程方式加载 Dojo 模块(错误示例)
myHandler: function(e){
  dojo.require("dijit.CalendarLite");
  var cal = new dijit.CalendarLite();
  ....
}

要避免这种问题,您可以编写 dojo["require"] 而不是 dojo.require,如清单 25 所示。

清单 25. 以编程方式加载 Dojo 模块(正确示例)
myHandler: function(e){
  dojo["require"]("dijit.CalendarLite");
  var cal = new dijit.CalendarLite();
  ....
}

如果正在异步加载您的应用程序,则语法与清单 26 显示的不同。但是,这也不是一个好示例,因为如果以这种方式编写代码,构建工具会意外地选择指定的模块。

清单 26. 以编程方式加载 Dojo 模块(错误示例)
myHandler: function(e){
  require(["dijit/CalendarLite"], lang.hitch(this, function(module){
    var cal = new module();
    ....
  }));
}

可以通过将类名称指定给变量来避免此构建问题,如清单 27 所示。

清单 27. 以编程方式加载 Dojo 模块(正确示例)
myHandler: function(e){
  var cls = "dijit/CalendarLite"; // assign to a variable so as not to be in the build
  require([cls], lang.hitch(this, function(module){
    var cal = new module();
    ....
  }));
}

异步模式

在 Dojo 1.7 中,除了传统的 Dojo 加载器,还提供了新 AMD (Asynchronous Module Definition) 加载器。Dojo Mobile 小部件以新 AMD 语法编写。要加载任意 Dojo 模块,您可以使用传统 dojo.require(清单 28)或新 API(清单 29)。

清单 28. dojo.require 的示例(传统)
<script src="../dojo/dojo.js" djConfig="parseOnLoad: true"></script>
<script language="JavaScript" type="text/javascript">
  dojo.require("dojox.mobile");
  dojo.require("dojox.mobile.parser");
  dojo.require("dojox.mobile.compat");
</script>
清单 29. require 的示例(AMD 语法)
<script src="../dojo/dojo.js" djConfig="async:true, parseOnLoad:true"></script>
<script language="JavaScript" type="text/javascript">
  require([
    "dojox/mobile",
    "dojox/mobile/parser",
    "dojox/mobile/compat"
  ]);
</script>

清单 28 同步加载了模块,而清单 29 则异步加载模块。在加载了大量 JavaScript 文件的情况下,预计异步加载比同步加载执行得更好。但是,如果将 JavaScript 文件合并到一个构建文件中,则同步加载和异步加载没有太大差别。

但是,您应该注意一个较大的差异。图 18 显示了打开 test_iPhone-Animation.html(以清单 28 所示的方式加载模块)时的传输文件,而图 19 显示了打开 test_iPhone-Animation-async.html(以清单 29 所示的方式加载模块)时的传输文件。如表 5 所示,在同步模式中,会额外加载 17 个文件,总共 26KB。这是因为同步模式以向后兼容性模式进行工作,因此需要加载更多必需模块来保持向后兼容性。假设 test_iPhone-Animation-async.html 在没有这些额外模块的情况下进行工作,显然不会使用并加载它们。因此,即使您慎重地选择了使用的模块,并使用 customBase 选项优化构建,使用 dojo.require() 可能会破坏您的工作。您应该使用 require API 而不是 dojo.require() 来优化应用程序的性能。

图 17. 同步加载 (test_iPhone-Animation.html)
同步加载
图 18. 异步加载 (test_iPhone-Animation-async.html)
异步加载
表 5. 同步模式和异步模式之间的资源下载比较
文件请求的数量总大小
test_iPhone-Animation.html2369.15 KB
test_iPhone-Animation-async.html643.54 KB

顺便说一下,即使您使用像清单 29 那样的 AMD 语法,如果您不指定 async:true(默认值是 false),则您的应用程序将在兼容性模式下运行并且会加载其他模块。


dojox.mobile.parser

dojox.mobile.parser 是 dojo.parser 的一个小子集。设计它纯粹是为了降低总体代码大小。它没有特定于移动的额外功能,除了 dojo.parser,因此没有理由使用 dojox.mobile.parser 代替 dojo.parser。您可以使用自己喜欢的功能。在 dojox.mobile.parser 中删除了 dojo.parser 的一些高级功能,比如 <script type="dojo/method"> 和 <script type="dojo/connect">,因此 dojox.mobile.parser 比较小。

如表 6 所示,dojox.mobile.parser 的大小大约为 0.7KB (minified & gzipped),而 dojo.parser(加上 dojox.mobile 基础模块不需要的其依赖关系模块)为 6.5KB。如果 dojox.mobile.parser 的功能对您的应用程序来说足够了,则使用它可降低应用程序的总体代码大小。

表 6. dojox.mobile.parser 和 dojo.parser 之间的大小比较
描述字节大小
dojox.mobile.parser734 字节
dojo.parser + 依赖关系模块6,507 字节

结束语

本文展示了如何使用 Dojo Mobile 构建轻量级移动 Web 应用程序,以及如何将此性能优化技术应用于您的应用程序。

参考资料

学习

获得产品和技术

  • Dojo Toolkit 下载页面 下载最新的 Dojo 1.7。
  • 下载和试用 IBM Mobile Technology Preview,这是一个代码示例和服务集合,帮助您开始构建扩展和集成到企业中的移动应用程序。预览版包括 RESTful 通知服务、PhoneGap(构建混合移动应用程序的开源框架)、轻量级 WebSphere Application Server 运行时和示例代码,支持您查看它们如何工作。
  • IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile 包括 IBM Dojo 1.7 Toolkit,这是一种移动的富 Internet 应用程序 (RIA) 构建块和基于 Dojo 的图形组件。使用随附提供的 Rational 工具,Feature Pack 有助于您利用最初针对桌面浏览器开发的 WebSphere 应用程序,然后改编它们并将它们部署到移动设备。
  • 以最适合您的方式 评估 IBM 产品:下载产品试用版,在线试用产品,在云环境下试用产品,或者在 SOA Sandbox 中花费几个小时来学习如何高效实现面向服务架构。

讨论

  • 加入 developerWorks 社区。查看开发人员推动的博客、论坛、组和 wikis,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


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


忘记密码?
更改您的密码

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

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

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

选择您的昵称



当您初次登录到 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=817202
ArticleTitle=使用 Dojo Mobile 开发轻量级移动 Web 应用程序
publish-date=05222012