posts - 21, comments - 456, trackbacks - 0, articles - 0
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言:

 

如果你第一次看Sencha Touch MVC应用程序的例子,很可能会被那一个个文件夹和一堆堆js文件搞得头昏脑胀,它们之间到底有什么关系?互相之间如何协作?这就是你在下手写代码之前必须搞清楚的第一件事了,本专题将为你解决这个困惑。

 

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

原文标题是:Managing Dependencies with MVC管理MVC依赖项

Sencha Touch 交流QQ213119459欢迎您的加入。


 

Managing Dependencies with MVC

管理MVC依赖项


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


There are two main places that dependencies can be defined in a Sencha Touch 2 app - on the application itself or inside the application classes. This guide gives some advice on how and where to declare dependencies in your app.

ST2应用程序用来定义依赖项的地方主要有两个,application本身和应用程序内部类。本指南将给出一些关于应用程序如何放置和在哪里放置依赖项的建议。


Application Dependencies

应用程序依赖项


When you create an MVC application, your Ext.application gives you a convenient way of specifying the models, views, controllers, stores and profiles that your application uses. Here's an example:

当你创建一个MVC应用程序时,Ext.application会提供一个直观的方式来设置应用程序会用到的数据模型、视图、控制器、数据存储器和配置文件等,如下例所示:


Ext.application({

    name: 'MyApp',

 

    views: ['Login'],

    models: ['User'],

    controllers: ['Users'],

    stores: ['Products'],

    profiles: ['Phone', 'Tablet']

});


These 5 configuration options are convenient ways to load the types of files that applications usually consist of - models, views, controllers, stores and profiles. Specifying these configurations means your application will automatically load the following files:

5个配置项是用来加载应用程序常用文件(数据模型、视图、控制器、存储器、配置文件)的快捷方式。如上的配置意味着应用程序会自动加载下列文件:

app/view/Login.js
app/model/User.js
app/controller/Users.js
app/store/Products.js
app/profile/Phone.js
app/profile/Tablet.js


In terms of what gets loaded, the example above is equivalent to defining dependencies manually like this:

就加载文件而言,上面的例子跟下面的定义是等价的:


Ext.require([

    'MyApp.view.Login',

    'MyApp.model.User',

    'MyApp.controller.Users',

    'MyApp.store.Products',

    'MyApp.profile.Phone',

    'MyApp.profile.Tablet'

]);


As you add more classes to your application, these configurations become more and more useful in helping you avoid typing out the full class names for every file. Be aware, however, that three of those configurations do more than just load files. As well as loading the files, they do the following:

在你需要加载更多的类文件情况下,这种配置方式就会更有用,它能避免你为每一个文件都拼写又臭又长的完整类名。除了把依赖文件加载进来之外,这几个配置还会做更多的事情:


l  profiles - instantiates each Profile and determines if it should be active. If so, the Profile's own dependencies are also loaded

配置文件 实例化每一个Profle并判断哪一个当前可用。当前可用的那个profile中所有依赖项也将被加载

l  controllers - instantiates each Controller after loading

控制器 加载完成后实例化每一个控制器

l  stores - instantiates each Store, giving it a default store ID if one is not specified

存储器 实例化每一个存储器,没有指定id的存储器会被指定一个默认id


What this means is that if you want to take advantage of all of the convenience MVC offers you, you're advised to use these configuration options when defining your application dependencies.

这意味着,如果你要享受MVC带给你的便利,那么载你定义应用程序依赖项的时候,最好使用配置选项这种方式。


Profile-specific Dependencies

配置文件指定的依赖项


When using Device Profiles, chances are it means you have some classes that are used only on certain devices. For example, the Tablet version of your app probably contains more functionality than the Phone version, which usually means it will need to load more classes. Additional dependencies can be specified inside each Profile:

当你使用设备配置的时候,可能会有一些类是仅在特定设备上使用的。例如,平板电脑版本的应用程序可能包含比手机版本更多的功能,这当然意味着要加载更多的类。每个Profile都可以在内部定义额外的依赖项。


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

    extend: 'Ext.app.Profile',

 

    config: {

        views: ['SpecialView'],

        controllers: ['Main'],

        models: ['MyApp.model.SuperUser']

    },

 

    isActive: function() {

        return Ext.os.is.Tablet;

    }

});


Now what's going to happen here is that the dependencies specified in each Profile are going to be loaded regardless of whether or not the Profile is active. The difference is, even though they're loaded, the Application knows not to do the additional processing like instantiating profile-specific Controllers if the profile is not active.

然后每个profile中定义的依赖项都会被加载,不管这个profile是否active,不过尽管都被加载,但应用程序不会去做类似实例化非active状态profile指定的控制器这样的无用功。


This probably sounds counter-intuitive - why download classes that aren't going to be used? The reason we do this is to produce a universal production build that can be deployed to any device, detect which profile it should use and then boot the app. The alternative is to create custom builds for each profile, craft a micro-loader than can detect which profile a device should activate and then download the code for that profile.

这听起来有点不合常规,为什么要下载那些不会用到的类文件呢?这么做的原因是产生一个可以在任何设备上运行的通用程序版本,然后检测哪一个profile应该被使用,接着从这个profile启动应用程序。与之相对的选择是为每个profile创建一个应用版本,然后启动一个微型加载器来检测哪个profile该被选择,然后去下载该profile需要的代码文件。


While the universal build approach does mean you're downloading code you don't need on every device, for the vast majority of apps this amounts to so little additional size it's difficult to detect the difference. For very large apps the difference will become more noticeable so it's possible we'll revisit this subject after 2.0.

的确这种通用架构的程序会在每个设备上都下载一些根本用不到的代码文件,不过对于绝大多数应用程序来说,你多下载的这点文件影响实在是微乎其微。而对于比较庞大的应用程序来说,这个问题可能更值得注意,所以我们可能在2.0的后续版本对它进行处理。


Nested Dependencies

级联依赖


For larger apps it's common to split the models, views and controllers into subfolders so keep the project organized. This is especially true of views - it's not unheard of for large apps to have over a hundred separate view classes so organizing them into folders can make maintenance much simpler.

大一些应用通常会把数据模型、视图、控制器分别存储在不同子文件夹下,这样可以让整个项目看起来更清晰明了一些。对于视图来说尤其如此,大型应用拥有上百个独立的视图类并非天方夜谭,因此分文件夹存储几乎不可避免。


To specify dependencies in subfolders just use a period (".") to specify the folder:

指定子文件夹中的依赖项只需要使用“.”来分割文件夹路径即可:


Ext.application({

    name: 'MyApp',

 

    controllers: ['Users', 'nested.MyController'],

    views: ['products.Show', 'products.Edit', 'user.Login']

});


In this case these 5 files will be loaded:

上例中将会加载下列5个文件

app/controller/Users.js
app/controller/nested/MyController.js
app/view/products/Show.js
app/view/products/Edit.js
app/view/user/Login.js


Note that we can mix and match within each configuration here - for each model, view, controller, profile or store you can specify either just the final part of the class name (if you follow the directory conventions), or the full class name.

我们可以混合使用两种方式来定义每个数据模型、视图、控制器、配置文件和存储器:快捷路径方式(符合mvc推荐原则的类只写最后的类名即可)和完整路径方式(自定义路径的类则写完整路径加类名)。


External Dependencies

外部依赖项


We can specify application dependencies from outside our application by fully-qualifying the classes we want to load. A common use case for this is sharing authentication logic between multiple applications. Perhaps you have several apps that login via a common user database and you want to share that code between them. An easy way to do this is to create a folder alongside your app folder and then add its contents as dependencies for your app.

我们可以通过指定想要加载的完整类名方式来定义应用程序之外的类作为依赖项。这种情况最常见的用途就是在多个应用之间共享认证逻辑。我们可能会有好几个应用程序都要到同一个用户数据库进行验证并实现登录,这时我们当然希望它们能够共享用户登录的代码。比较容易的方式就是在应用程序文件夹之外创建一个单独的文件夹然后把其中的内容作为依赖项添加到应用程序中去。


For example, let's say our shared login code contains a login controller, a user model and a login form view. We want to use all of these in our application:

我们假定共享的登录代码包含一个login控制器,一个用户model和一个login表单视图。我们要在应用程序中把它们全部用上:


Ext.Loader.setPath({

    'Auth': 'Auth'

});

 

Ext.application({

    views: ['Auth.view.LoginForm', 'Welcome'],

    controllers: ['Auth.controller.Sessions', 'Main'],

    models: ['Auth.model.User']

});


This will load the following files:

上述代码将加载以下的文件:

Auth/view/LoginForm.js
Auth/controller/Sessions.js
Auth/model/User.js
app/view/Welcome.js
app/controller/Main.js


The first three were loaded from outside our application, the last two from the application itself. Note how we can still mix and match application files and external dependency files.

前面三个文件加载自应用程序外部,后两个则来自应用程序内部。同样我们可以混合调用内外部依赖项。


Note that to enable the loading of external dependencies we just have to tell the Loader where to find those files, which is what we do with the Ext.Loader.setPath call above. In this case we're telling the Loader to find any class starting with the 'Auth' namespace inside our 'Auth' folder. This means we can drop our common Auth code into our application alongside the app folder and the framework will be able to figure out how to load everything.

想要启用外部依赖项加载,我们只需告诉Loader到哪里可以找到这些文件即可,Ext.Loader.setPath就是干这个的。上例中我们告诉Loader所有以Auth命名空间中的文件都可以到Auth这个文件夹中找到。这样我们就能把应用程序文件夹之外的通用验证代码都拽进来了,其他的事情由ST框架来处理。


Where Each Dependency Belongs

依赖项应该放在哪里


The general rule when deciding where to declare each dependency is to keep your classes completely self-contained. For example, if you have a view that contains several other views, you should declare those dependencies inside the view class, not the application:

决定在哪里声明依赖项的一个基本原则就是保证你的类文件完整的内部包含。例如,你有一个视图A包含了几个其他的视图,你就应该在这个A视图内部声明它的依赖项,而不是在application中:


Ext.define('MyApp.view.Main', {

    extend: 'Ext.Container',

 

    requires: [

        'MyApp.view.Navigation',

        'MyApp.view.MainList'

    ],

 

    config: {

        items: [

            {

                xtype: 'navigation'

            },

            {

                xtype: 'mainlist'

            }

        ]

    }

});


And in your app.js:

App.js中这么写:


Ext.application({

    views: ['Main']

});


This is the best way to declare those dependencies for two reasons - it keeps your app.js clean and enables you to reliably require your MyApp.view.Main and know that it already has all of its dependencies satisfied. The alternative would be to list all of your views inside your app.js like this:

这才是依赖项的最佳声明方式。两个原因:1、确保app.js干净;2、让你知道主程序依赖MyApp.view.Main就已经足够。不好的方式就是下面这样把视图都罗列在app.js里:


//this is bad

Ext.application({

    views: ['Main', 'Navigation', 'MainList']

});


A simple way of thinking about this is that app.js just contains top-level views. If you use Ext.create('MyApp.view.SomeView') inside your app, that view can be considered top-level. If a view is only ever constructed as a sub-view of another view (as with MyApp.view.Navigation and MyApp.view.MainList above), it doesn't belong in app.js.

换种方式来描述这个问题,app.js只需要包含最顶级的视图即可。你在应用程序内部通过Ext.create('MyApp.view.SomeView')来创建的视图就可以视作顶级视图。其他那些仅仅被作为某些视图内部子视图的(比如例子中的MyApp.view.NavigationMyApp.view.MainList)就不应该出现在app.js里面。


Changes since 1.x

1.x版本的不同之处


In Sencha Touch 1, dependencies were often specified in Controllers as well as in the Ext.application call. While this offered some conveniences, it also masked the true architecture of the system and coupled views, models and stores too closely to controllers. Here's what you could do in 1.x:

ST1中,依赖项经常在控制器中指定(就跟Ext.application中那样)。这样的确提供了一些便利,但同时也模糊了系统的真实架构,而且视图、数据模型和数据存储器与控制器之间靠得太紧密。在1.x中我们是这么做的:


//1.x code, deprecated

Ext.regController('SomeController', {

    views: ['Login'],

    models: ['User'],

    stores: ['Products']

});


This is exactly the same as defining the views, models and stores inside Ext.application, but also gave some convenience methods for accessing those classes inside your controller. 1.x generated two functions - getLoginView() and getUserModel() - and exposed a getStore() function that returned a reference to any of the Stores you defined in this Controller. In 2.x these functions no longer exist, but it's easy to use the alternatives.

这种方式跟Ext.application中定义视图、数据模型和数据存储器是一样的,这么做的确也为控制器内部访问这些类提供了很方便的方法。ST1.x框架会自动产生两个方法getLoginView()getUserModel(),还有一个getStore()函数可以返回对控制器中定义的任意数据存储器的引用。在ST2.x中这些函数都不复存在,但是也提供了很便利的方法。


In each case here the first line refers to Sencha Touch 1.x, with the second line showing the 2.x way:

下面例子的第一行是ST1.x的写法,第二行是ST2.x的写法:


//creating a view - 2.x uses the standardized Ext.create

this.getLoginView().create();

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

 

//getting a Model - just type out the Model name (it's shorter and faster)

this.getUserModel();

MyApp.model.User;

 

//Ext.getStore can access any Store whereas the old this.getStore only

//accessed those Stores listed in your Controller

this.getStore('Products');

Ext.getStore('Products');


Removing these functions speeds up application launching because the framework no longer needs to generate one function for each model and view defined in each Controller. It also means that the conventions for MVC match the conventions for the rest of the framework, leading to a more predictable API.

去掉这些方法加速了应用程序的运行速度,因为ST框架无需再为控制器中的每个视图和数据模型来生成一个方法了。这就意味着使用MVC的便利之处同样也对其他框架有效,从而使得整个API更加受控。