博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言: 

读英文文档总是很难的,非母语且专业化的长篇大论容易令人气馁和浮躁,从而导致学习效率低下。

翻译是一个好方法,把学习的需求转变成翻译的任务,强迫自己不仅要看进去、看得懂,而且还要字斟句酌,然后清晰明了的表达出来,这种在翻译中学习的成果无疑是最牢固的,最后你的译文还可以帮助别人。 

这篇文章的英文原址是http://docs.sencha.com/touch/2-0/#!/guide/apps_intro

原文标题是:Intro to Applications with Sencha Touch 2Sencha Touch 2 应用程序简介)。有意思的是,官方文档目录中给它的说明是 All about Applications(关于应用程序的一切) , 仔细读来你会发现这篇文章其实是Sencha Touch MVC的总纲,对于理解Sencha Touch的MVC模式意义重大,不得不读,所以我把它作为此系列翻译的第一篇文章。鉴于本人水平有限,翻译不当乃至谬误之处在所难免,还望大家不吝赐教。 

这篇原创翻译首先发布在自己的博客上,后续更新也都会在第一时间发布上去,敬请关注。另外我还建了一个QQ群(群号213119459)方便Sencha Touch爱好者交流,欢迎您的加入。

译文原地址:http://www.cnblogs.com/dowinning/archive/2012/02/14/2350303.html

 

 

Intro to Applications with Sencha Touch 2

Sencha Touch 2 应用程序简介


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


Sencha Touch 2 is optimized around building applications that work across multiple platforms. To make the writing of applications as simple as possible, we provide a simple but powerful application architecture that leverages the MVC (Model View Controller) pattern. This approach keeps your code clean, testable and easy to maintain, and provides you with a number of benefits when it comes to writing your apps:

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

  • History Support: full back button support inside your app, and any part of your app can be linked to 
    访问历史支持:你的应用将获得完整的后退按钮功能,其中的任意部分都可以被链接到
  • Deep Linking: share deep links that open any screen in your app, just like linking to a web page 
    深度链接:如同以往链接到某一个web页面一样,深度链接可以打开你应用程序中的任意屏幕
  • Device Profiles: easily customize your application's UI for phones, tablets and other devices while sharing all of the common code 
    设备配置文件:共享通用代码的同时,轻松为手机、平板电脑还有其他设备定制应用程序UI(用户界面)

 

 

Anatomy of an Application

应用程序结构解析 

 



An Application is a collection of Models, Views, Controllers, Stores and Profiles, plus some additional metadata specifying things like application icons and launch screen images.

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

您的浏览器可能不支持显示此图像。

  • Models: represent a type of object in your app - for example an e-commerce app might have models for Users, Products and Orders 
    数据模型:在应用程序中表示一种数据模型,比如一个电子商务应用程序可能会有用户、产品、订单等不同的数据模型
  • Views: are responsible for displaying data to your users and leverage the built in Components in Sencha Touch 
    视图:负责将数据展示给用户,并扩充 Sencha Touch 的内置组件。(译者注:你可以理解为用户界面的一个个组成部分)
  • Controllers: handle interaction with your application, listening for user taps and swipes and taking action accordingly 
    控制器:处理应用程序的交互,侦听用户的轻触、猛击(译者注:真实意思是长按?)等事件并做出相应的响应 
  • Stores: are responsible for loading data into your app and power Components like Lists and DataViews 
    数据存储器:负责把数据加载到你的应用并以列表(List)或者数据视图(DataView)等形式表现出来
  • Profiles: enable you to easily customize your app's UI for tablets and phones while sharing as much code as possible 
    设备配置:可以使你为平板电脑和手机等不同设备,轻易定制应用程序用户界面,并尽可能多的共享代码 

 
The Application is usually the first thing you define in a Sencha Touch application, and looks something like this:

Application 对象通常是你开发一个ST应用时需要定义的第一个东西,类似下面这样子:

Ext.application({

    name: 'MyApp',

    models: ['User', 'Product', 'nested.Order'],

    views: ['OrderList', 'OrderDetail', 'Main'],

    controllers: ['Orders'], 

    launch: function() {

        Ext.create('MyApp.view.Main');

    }

});

 
The name is used to create a single global namespace for your entire application, including all of its models, views, controllers and other classes. For example, an app called MyApp should have its constituent classes follow the pattern MyApp.model.UserMyApp.controller.Users,MyApp.view.Main etc. This keeps your entire app under a single global variable so minimizes the chance of other code on the page conflicting with it.

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

The Application uses the defined modelsviews and controllers configurations to automatically load those classes into your app. These follow a simple file structure convention - models are expected to be in the app/model directory, controllers in the app/controller directory and views inside the app/view directory - for example app/model/User.jsapp/controllers/Orders.js and app/view/Main.js.

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

Note that one of the models we specified was different to the others - we specified the full class name ("MyApp.model.nested.Order"). We're able to specify the full class name for any of those configurations if we don't follow the normal naming conventions. See the Dependencies section of the Ext.app.Application docs for full details on how to specify custom dependencies.

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



Controllers

控制器 

 



Controllers are the glue the binds an application together. They listen for events fired by the UI and then take some action on it. This helps to keep our code clean and readable, and separates the view logic from the control logic.

Controller(控制器)就像胶水一样粘合出一个完整的应用程序,它们侦听UI界面触发的事件,然后做出相应的动作,还能够让我们的代码更简洁,可读性好,从而把界面逻辑和控制逻辑隔离开来。

For example, let's say you require users to log in to your app via a login form. The view in this case is the form with all of its fields and other controls. A controller should listen to tap event on the form's submit button and then perform the authentication itself. Any time we deal with manipulating data or state, the controller should be the class that activates that change, not a view.

假如你需要用户通过一个 login 表单来登录你的应用程序,此时的 view(视图)就是这个包含了所有字段和其他元素的表单,而它的 controller(控制器)要做的就是侦听表单提交按钮的点触事件并进行身份验证,每次我们要处理数据或者状态的时候,这个 controller(控制器)才是应该起作用的类,而不是 view(视图)。

Controllers expose a small but powerful set of features, and follow a few simple conventions. Each Controller in your application is a subclass of Ext.app.Controller (though you can subclass existing Controllers, so long as it inherits from Ext.app.Controller at some point). Controllers exist in the MyApp.controller.* namespace - for example if your app had a Sessions controller it would be called MyApp.controller.Sessions and exist in the file app/controller/Sessions.js.

Controller(控制器)通过一些简单的约定,展示了一套虽小但却很强大的特性。应用程序的每一个 controller(控制器)都是 Ext.app.Controller 的一个子类,当然你也可以继承现有的 controller ,只要它也是继承自 Ext.app.Controller controller(控制器)都存在于 MyApp.controller.* 命名空间,例如你的应用有一个叫做 Sessions controller ,那么它的完整命名空间将会是 MyApp.controller.Sessions 并且被定义在 app/controller/Sessions.js 文件中。

Although each Controller is a subclass of Ext.app.Controller, each one is instantiated just once by the Application that loaded it. There is only ever one instance of each Controller at any one time and the set of Controller instances is managed internally by the Application. Using Application's controllers config (as we do above) loads all of the Controllers and instantiates them automatically.

每个 controller(控制器)都是 Ext.app.Controller 的一个子类,加载它的应用程序也只会对它实例化一次,应用程序自己会控制每个 controller(控制器)在同一时间只有一个实例。我们使用 Application 对象的 controllers 参数来加载 Controller(控制器)并且会自动实例化它们。


A simple example

一个简单例子

Here's how we might quickly define the Sessions controller described above. We're using 2 Controller configurations here - refs and control. Refs are an easy way to find Components on your page - in this case the Controller will look for all Components that match the formpanel xtype and assign the first one it finds to the loginForm property. We'll use that property in the doLogin function later.

这里将演示如何快速定义上面描述的 Sessions 控制器,我们将使用 controller 的两个配置参数,refs 和 control ,refs 是找到页面组件的简单方式,这个例子里 controller(控制器)会搜索所有 formpanel 类型的控件,然后将找到的第一个赋值给 loginForm 属性,这个属性会在下面的 doLogin 方法中用到。

The second thing it does is set up a control configuration. Just like refs, this uses a ComponentQuery selector to find all formpanel xtypes that contain a button inside them (for example, this will find the Submit button in our hypothetical login form). Whenever any button of this type fires its tap event, our Controller's doLogin function will be called:

然后我们要做的是配置 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);

    }

});

 
The doLogin function itself is quite straightforward. Because we defined a 'loginForm' ref, the Controller automatically generates agetLoginForm function that returns the formpanel that it matches. Once we have that form reference we just pull the values (username and password) out of it and pass them to an authenticate function. That's most of what Controllers ever do - listen for events fired (usually by the UI) and kick off some action - in this case authenticating.

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

For more on what Controllers are and what capabilities they possess see the controllers guide.

想了解更多关于 controller(控制器)是什么和能做什么的知识,请查看 controllers guide (控制器指南)页面。 



Stores

数据存储器 

 



Stores are an important part of Sencha Touch and power most of the data-bound widgets. At its simplest, a Store is not much more than an array of Model instances. Data-bound Components like List and DataView just render one item for each Model instance in the Store. As Model instances are added or removed from the Store events are fired, which the data-bound Components listen to and use to update themselves.

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

While the Stores guide has much more information on what Stores are and how they fit in with Components in your app, there are a couple of specific integration points with your Application instance that you should be aware of.

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



Device Profiles

设备配置文件 

 



Sencha Touch operates across a wide range of devices with differing capabilities and screen sizes. A user interface that works well on a tablet may not work very well on a phone and vice versa so it makes sense to provide customized views for different device types. However, we don't want to have to write our application multiple times just to provide a different UI - we'd like to share as much code as possible.

ST 可以跨越非常广泛的平台,尽管这些平台拥有不同的性能和屏幕尺寸。一个在平板电脑上工作良好的UI并不一定适应手机界面,反之亦然。所以为不同设备提供定制过的不同 view(视图)是一件很有必要的事情。毕竟谁也不想仅仅为了提供不同的 UI 就得把应用程序重写 N 次,我们希望可以让不同设备共享尽可能多的代码。

Device Profiles are simple classes that enable you to define the different types of devices supported by your app and how they should be handled differently. They are opt-in, so you can develop your app without profiles at first and add them in later, or never use them at all.Each profile simply defines an isActive function that should return true if that profile should be active on the current device, plus a set of additionalmodelsviews and controllers to load if that profile is detected.

Device Profile(设备配置)是一些简单的类,这些类能让你定义程序支持的不同类型设备以及如何处理这些不同。Device Profile(设备配置)不是必需的,你可以一开始不定义Profile 以后再添加,甚至永远不定义它们。每个 profile 都要定义一个简单的 isActive 函数,用来返回当前设备上是否应该启用此 profile(换言之,该 profile 是否匹配当前的设备) ,并为该 profile 载入一堆(当前 profile 中约定的)model ,view 和 controller 。

To app Profile support to your app you just need to tell your Application about those Profiles and then create Ext.app.Profile subclasses for them:

要为你的应用程序添加 Profile 支持功能(怀疑英文原文第一个 app 应为 add ),你只需告诉应用程序有哪些 profile 需要被支持,然后为它们各自创建Ext.app.Profile 的子类即可。

Ext.application({

    name: 'MyApp',

    profiles: ['Phone', 'Tablet'], 

    //as before

});

 
By defining the profiles above the Application will load app/profile/Phone.js and app/profile/Tablet.js. Let's say that the tablet version of the app enables additional capabilities - for example managing groups. Here's an example of how we might define the Tablet profile:

如上面代码所示,应用程序会加载 app/profile/Phone.js 和 app/profile/Tablet.js 两个文件,我们假定平板电脑的版本将会拥有一些额外的能力,比如对组的管理功能,下面的例子将告诉我们该如何定义tablet的profile:

Ext.define('MyApp.profile.Tablet', {

    extend: 'Ext.app.Profile', 

    config: {

        controllers: ['Groups'],

        views: ['GroupAdmin'],

        models: ['MyApp.model.Group']

   }, 

   isActive: function() {

       return Ext.os.is.Tablet;

   
}); 

 
The isActive function will return true whenever the application is run on what Sencha Touch determines to be a tablet. This is a slightly subjective determination because there is a near-continuous spectrum of device shapes and sizes with no clear cutoff between phones and tablets. Because there is no foolproof way to state which devices are tablets and which are phones, Sencha Touch's Ext.os.is.Tablet is set to true when running on an iPad and false otherwise. If you need more fine grained control it's easy to provide any implementation you like inside your isActive function, so long as it returns true or false.

当ST检测到你的设备是一台平板电脑时,isActive 函数将会返回 true 。鉴于现在不断涌现出的各种新设备,其外形和尺寸已经越来越模糊了手机和平板电脑的界限,故而我们无法找到傻瓜化的方式去界定哪些设备是平板哪些设备是手机,ST 的 Ext.os.is.Tablet 只有在 iPad 上运行的时候将被设定为 true ,其他则为 false ,如果你需要更好的判断和控制,你可以通过在 isActive 函数中进行更多你希望的检测,来控制它返回 true 还是 false 。

You should make sure that only one of your Profiles returns true from its isActive function. If more than one of them returns true, only the first one that does so will be counted and the rest ignored. The first one that returns true will be set as the Application's currentProfile, which can be queried at any time.

你必须保证只有一个 profile 的 isActive 函数可以返回 true ,如果超过一个的话,只有第一个会有效而其他将被忽略,第一个返回 true 的 profile 将被视作应用程序的 currentProfile(当前 profile ),你也可以随时获取到当前 profile 的值。

If the detected currentProfile has defined additional models, views, controllers and stores these will be automatically loaded by the Application, along with all of the modelsviews and controllers defined on the Application itself. However, all of the dependencies named in the Profile will be prepended with the Profile name unless the fully-qualified class name is provided. For example:

如果检测到的当前profile定义了额外的 model(数据模型)、view(视图)、controller(控制器)、store(数据存储器),它们会与 application 当中定义的其他元素一起被自动加载。而所有在 profile 中定义的元素路径前面都会被加上 profile 的名称,除非你对它们定义了完整路径的类名,如下所示。 

  • views: ['GroupAdmin'] will load app/view/tablet/GroupAdmin.js 
    views: ['GroupAdmin'] 将会加载 app/view/tablet/GroupAdmin.js (因为 GroupAdmin 是在 tablet profile 中配置的)
  • controllers: ['Groups'] will load app/controller/tablet/Groups.js 
    controllers: ['Groups'] 将会加载 app/controller/tablet/Groups.js (同上)
  • models: ['MyApp.model.Group'] will load app/model/Group.js 
    models: ['MyApp.model.Group'] 将会加载 app/model/Group.js (因为使用了完整路径)

 
Most of the time a Profile will only define additional controllers and views as the models and stores are typically shared between all variants of the app. For a more detailed discussion of Profiles see the device profiles guide.

绝大多数情况下,profile 只会定义额外的 controller 和 view ,因为 model 和 store 一般情况下都会被共享。关于 profile 的更多细节,请访问 device profiles guide 页面(设备配置指南)。 



Launch Process

ApplicationLaunch 

 



Each Application can define a launch function, which is called as soon as all of your app's classes have been loaded and the app is ready to be launched. This is usually the best place to put any application startup logic, typically creating the main view structure for your app.

每个 Application 对象都会定义一个 launch 函数,它将会在你应用程序所需的全部 class 加载完成,且应用程序已经做好准备的情况下执行。一般来说这里就是你用来放置应用程序启动逻辑的最好位置了,比如你可以在这里为你的应用创建主要 view 框架。

In addition to the Application launch function, there are two other places you can put app startup logic. Firstly, each Controller is able to define an init function, which is called before the Application launch function. Secondly, if you are using Device Profiles, each Profile can define a launch function, which is called after the Controller init functions but before the Application launch function.

除了 Application 中的 launch 函数之外,还有两个地方可以放置启动逻辑:第一,每个 controller(控制器)都可以定义一个 init 函数,这个函数将会运行在 application 的 launch 运行之前;第二,如果你使用了设备 profile ,每一个 profile 都可以定义一个 launch 函数,他将会在 controller(控制器)的 init 之后和 application 的 launch 之前被调用。

Note that only the active Profile has its launch function called - for example if you define profiles for Phone and Tablet and then launch the app on a tablet, only the Tablet Profile's launch function is called.

注意只有活动 profile 的 launch 函数才会被调用,比如你分别定义了 phone 和 tablet 的 profile ,现在是在 tablet 上运行它,那么只有 tablet profile 中的 launch 函数会被调用到。

  1. Controller#init functions called 
    Controller init 首先被调用
  2. Profile#launch function called 
    其次是当前 Profile launch 被调用 
  3. Application#launch function called 
    然后 Application launch 被调用 
  4. Controller#launch functions called 
    最后是其他 controller launch 被调用

 
When using Profiles it is common to place most of the bootup logic inside the Profile launch function because each Profile has a different set of views that need to be constructed at startup.

当使用 profiles 的时候,最好把启动逻辑代码放在 profile 的 launch 里面,因为每个 profile 可能需要调用不同的 view 来构建启动界面。 



Routing and History Support

路由和访问历史支持 

 



Sencha Touch 2 has full Routing and History support. Several of the SDK examples, including the Kitchen Sink, use the history support to enable the back button to easily navigate between screens - especially useful on Android.

ST2 具有完整的路由和访问历史支持,SDK 中的好几个例子,包括 Kitchen Sink ,都使用了历史路径支持,以实现通过 back 按钮可以轻易的在屏幕之间回退导航,这一点在 Android 上尤其有用。

There will be full documentation on the history support from beta 1 onwards. As of 2.0.0 PR4 the best place to learn about Sencha Touch 2's history support is kitchen sink example, which features lots of documentation on the routing and state restoration required for history support.

Beta 1 中有关于访问历史支持的完整文档,对于 2.0.0 PR4 来说,学习 ST2 的这个功能的最好地方是 Kitchen Sink 例子,这个例子在路由和状态返回等需要访问历史支持的地方有很多说明。 



Further Reading

延伸阅读 

 



There are several more guides on using the application architecture with Sencha Touch 2:

关于 ST2 的应用程序架构方面,还有几个其他的学习页面供参考。

 

全文完,尊重他人劳动,转载请注明出处,有问题烦请指正,多谢。