ManagingDependencies with MVC (管理MVC依赖项)
注:为方便起见,文中所有出现 Sencha Touch的地方均以 ST简写替代。
There are two main places thatdependencies can be defined in a Sencha Touch 2 app - on the application itselfor inside the application classes. This guide gives some advice on how andwhere to declare dependencies in your app.
ST2应用程序用来定义依赖项的地方主要有两个,application本身和应用程序内部类。本指南将给出一些关于应用程序如何放置和在哪里放置依赖项的建议。
ApplicationDependencies
应用程序依赖项
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 anexample:
当你创建一个MVC应用程序时,Ext.application会提供一个直观的方式来设置应用程序会用到的数据模型、视图、控制器、数据存储器和配置文件等,如下例所示:
These 5 configuration options areconvenient ways to load the types of files that applications usually consist of- models, views, controllers, stores and profiles. Specifying theseconfigurations means your application will automatically load the followingfiles:
这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, theexample above is equivalent to defining dependencies manually like this:
就加载文件而言,上面的例子跟下面的定义是等价的:
As you add more classes to yourapplication, these configurations become more and more useful in helping youavoid typing out the full class names for every file. Be aware, however, thatthree of those configurations do more than just load files. As well as loadingthe files, they do the following:
在你需要加载更多的类文件情况下,这种配置方式就会更有用,它能避免你为每一个文件都拼写又臭又长的完整类名。除了把依赖文件加载进来之外,这几个配置还会做更多的事情:
l profiles - instantiates each Profileand determines if it should be active. If so, the Profile's own dependenciesare also loaded
配置文件 – 实例化每一个Profle并判断哪一个当前可用。当前可用的那个profile中所有依赖项也将被加载
l controllers - instantiates eachController 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 wantto take advantage of all of the convenience MVC offers you, you're advised touse these configuration options when defining your application dependencies.
这意味着,如果你要享受MVC带给你的便利,那么载你定义应用程序依赖项的时候,最好使用配置选项这种方式。
Profile-specificDependencies
配置文件指定的依赖项
When using Device Profiles, chancesare it means you have some classes that are used only on certain devices. Forexample, the Tablet version of your app probably contains more functionalitythan the Phone version, which usually means it will need to load more classes.Additional dependencies can be specified inside each Profile:
当你使用设备配置的时候,可能会有一些类是仅在特定设备上使用的。例如,平板电脑版本的应用程序可能包含比手机版本更多的功能,这当然意味着要加载更多的类。每个Profile都可以在内部定义额外的依赖项。
Now what's going to happen here isthat the dependencies specified in each Profile are going to be loadedregardless of whether or not the Profile is active. The difference is, eventhough they're loaded, the Application knows not to do the additionalprocessing like instantiating profile-specific Controllers if the profile isnot active.
然后每个profile中定义的依赖项都会被加载,不管这个profile是否active,不过尽管都被加载,但应用程序不会去做类似实例化非active状态profile指定的控制器这样的无用功。
This probably soundscounter-intuitive - why download classes that aren't going to be used? Thereason we do this is to produce a universal production build that can bedeployed to any device, detect which profile it should use and then boot theapp. The alternative is to create custom builds for each profile, craft amicro-loader than can detect which profile a device should activate and thendownload the code for that profile.
这听起来有点不合常规,为什么要下载那些不会用到的类文件呢?这么做的原因是产生一个可以在任何设备上运行的通用程序版本,然后检测哪一个profile应该被使用,接着从这个profile启动应用程序。与之相对的选择是为每个profile创建一个应用版本,然后启动一个微型加载器来检测哪个profile该被选择,然后去下载该profile需要的代码文件。
While the universal build approachdoes mean you're downloading code you don't need on every device, for the vastmajority of apps this amounts to so little additional size it's difficult todetect the difference. For very large apps the difference will become morenoticeable so it's possible we'll revisit this subject after 2.0.
的确这种通用架构的程序会在每个设备上都下载一些根本用不到的代码文件,不过对于绝大多数应用程序来说,你多下载的这点文件影响实在是微乎其微。而对于比较庞大的应用程序来说,这个问题可能更值得注意,所以我们可能在2.0的后续版本对它进行处理。
NestedDependencies
级联依赖
For larger apps it's common to splitthe models, views and controllers into subfolders so keep the projectorganized. This is especially true of views - it's not unheard of for largeapps to have over a hundred separate view classes so organizing them intofolders can make maintenance much simpler.
大一些应用通常会把数据模型、视图、控制器分别存储在不同子文件夹下,这样可以让整个项目看起来更清晰明了一些。对于视图来说尤其如此,大型应用拥有上百个独立的视图类并非天方夜谭,因此分文件夹存储几乎不可避免。
To specifydependencies in subfolders just use a period (".") to specify thefolder:
指定子文件夹中的依赖项只需要使用“.”来分割文件夹路径即可:
In this case these 5 files will beloaded:
上例中将会加载下列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 matchwithin each configuration here - for each model, view, controller, profile orstore you can specify either just the final part of the class name (if you followthe directory conventions), or the full class name.
我们可以混合使用两种方式来定义每个数据模型、视图、控制器、配置文件和存储器:快捷路径方式(符合mvc推荐原则的类只写最后的类名即可)和完整路径方式(自定义路径的类则写完整路径加类名)。
ExternalDependencies
外部依赖项
We can specify applicationdependencies from outside our application by fully-qualifying the classes wewant to load. A common use case for this is sharing authentication logicbetween multiple applications. Perhaps you have several apps that login via acommon user database and you want to share that code between them. An easy wayto do this is to create a folder alongside your app folder and then add itscontents as dependencies for your app.
我们可以通过指定想要加载的完整类名方式来定义应用程序之外的类作为依赖项。这种情况最常见的用途就是在多个应用之间共享认证逻辑。我们可能会有好几个应用程序都要到同一个用户数据库进行验证并实现登录,这时我们当然希望它们能够共享用户登录的代码。比较容易的方式就是在应用程序文件夹之外创建一个单独的文件夹然后把其中的内容作为依赖项添加到应用程序中去。
For example, let's say our sharedlogin code contains a login controller, a user model and a login form view. Wewant to use all of these in our application:
我们假定共享的登录代码包含一个login控制器,一个用户model和一个login表单视图。我们要在应用程序中把它们全部用上:
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 fromoutside our application, the last two from the application itself. Note how wecan still mix and match application files and external dependency files.
前面三个文件加载自应用程序外部,后两个则来自应用程序内部。同样我们可以混合调用内外部依赖项。
Note that to enable the loading ofexternal dependencies we just have to tell the Loader where to find thosefiles, which is what we do with the Ext.Loader.setPath call above. In this casewe're telling the Loader to find any class starting with the 'Auth' namespaceinside our 'Auth' folder. This means we can drop our common Auth code into ourapplication alongside the app folder and the framework will be able to figureout how to load everything.
想要启用外部依赖项加载,我们只需告诉Loader到哪里可以找到这些文件即可,Ext.Loader.setPath就是干这个的。上例中我们告诉Loader所有以Auth命名空间中的文件都可以到Auth这个文件夹中找到。这样我们就能把应用程序文件夹之外的通用验证代码都拽进来了,其他的事情由ST框架来处理。
WhereEach Dependency Belongs
依赖项应该放在哪里
The general rulewhen deciding where to declare each dependency is to keep your classescompletely self-contained. For example, if you have a view that containsseveral other views, you should declare those dependencies inside the viewclass, not the application:
决定在哪里声明依赖项的一个基本原则就是保证你的类文件完整的内部包含。例如,你有一个视图A包含了几个其他的视图,你就应该在这个A视图内部声明它的依赖项,而不是在application中:
And in your app.js:
App.js中这么写:
This is the best way to declarethose dependencies for two reasons - it keeps your app.js clean and enables youto reliably require your MyApp.view.Main and know that it already has all ofits dependencies satisfied. The alternative would be to list all of your viewsinside your app.js like this:
这才是依赖项的最佳声明方式。两个原因:1、确保app.js干净;2、让你知道主程序依赖MyApp.view.Main就已经足够。不好的方式就是下面这样把视图都罗列在app.js里:
A simple way of thinking about thisis that app.js just contains top-level views. If you useExt.create('MyApp.view.SomeView') inside your app, that view can be consideredtop-level. If a view is only ever constructed as a sub-view of another view (aswith MyApp.view.Navigation and MyApp.view.MainList above), it doesn't belong inapp.js.
换种方式来描述这个问题,app.js只需要包含最顶级的视图即可。你在应用程序内部通过Ext.create('MyApp.view.SomeView')来创建的视图就可以视作顶级视图。其他那些仅仅被作为某些视图内部子视图的(比如例子中的MyApp.view.Navigation和MyApp.view.MainList)就不应该出现在app.js里面。
Changessince 1.x
跟1.x版本的不同之处
In Sencha Touch 1, dependencies wereoften specified in Controllers as well as in the Ext.application call. Whilethis offered some conveniences, it also masked the true architecture of thesystem and coupled views, models and stores too closely to controllers. Here'swhat you could do in 1.x:
在ST1中,依赖项经常在控制器中指定(就跟Ext.application中那样)。这样的确提供了一些便利,但同时也模糊了系统的真实架构,而且视图、数据模型和数据存储器与控制器之间靠得太紧密。在1.x中我们是这么做的:
注:为方便起见,文中所有出现 Sencha Touch的地方均以 ST简写替代。
There are two main places thatdependencies can be defined in a Sencha Touch 2 app - on the application itselfor inside the application classes. This guide gives some advice on how andwhere to declare dependencies in your app.
ST2应用程序用来定义依赖项的地方主要有两个,application本身和应用程序内部类。本指南将给出一些关于应用程序如何放置和在哪里放置依赖项的建议。
ApplicationDependencies
应用程序依赖项
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 anexample:
当你创建一个MVC应用程序时,Ext.application会提供一个直观的方式来设置应用程序会用到的数据模型、视图、控制器、数据存储器和配置文件等,如下例所示:
Ext.application({
name: 'MyApp',
views: ['Login'],
models: ['User'],
controllers: ['Users'],
stores: ['Products'],
profiles: ['Phone','Tablet']
});
These 5 configuration options areconvenient ways to load the types of files that applications usually consist of- models, views, controllers, stores and profiles. Specifying theseconfigurations means your application will automatically load the followingfiles:
这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, theexample 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 yourapplication, these configurations become more and more useful in helping youavoid typing out the full class names for every file. Be aware, however, thatthree of those configurations do more than just load files. As well as loadingthe files, they do the following:
在你需要加载更多的类文件情况下,这种配置方式就会更有用,它能避免你为每一个文件都拼写又臭又长的完整类名。除了把依赖文件加载进来之外,这几个配置还会做更多的事情:
l profiles - instantiates each Profileand determines if it should be active. If so, the Profile's own dependenciesare also loaded
配置文件 – 实例化每一个Profle并判断哪一个当前可用。当前可用的那个profile中所有依赖项也将被加载
l controllers - instantiates eachController 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 wantto take advantage of all of the convenience MVC offers you, you're advised touse these configuration options when defining your application dependencies.
这意味着,如果你要享受MVC带给你的便利,那么载你定义应用程序依赖项的时候,最好使用配置选项这种方式。
Profile-specificDependencies
配置文件指定的依赖项
When using Device Profiles, chancesare it means you have some classes that are used only on certain devices. Forexample, the Tablet version of your app probably contains more functionalitythan 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 isthat the dependencies specified in each Profile are going to be loadedregardless of whether or not the Profile is active. The difference is, eventhough they're loaded, the Application knows not to do the additionalprocessing like instantiating profile-specific Controllers if the profile isnot active.
然后每个profile中定义的依赖项都会被加载,不管这个profile是否active,不过尽管都被加载,但应用程序不会去做类似实例化非active状态profile指定的控制器这样的无用功。
This probably soundscounter-intuitive - why download classes that aren't going to be used? Thereason we do this is to produce a universal production build that can bedeployed to any device, detect which profile it should use and then boot theapp. The alternative is to create custom builds for each profile, craft amicro-loader than can detect which profile a device should activate and thendownload the code for that profile.
这听起来有点不合常规,为什么要下载那些不会用到的类文件呢?这么做的原因是产生一个可以在任何设备上运行的通用程序版本,然后检测哪一个profile应该被使用,接着从这个profile启动应用程序。与之相对的选择是为每个profile创建一个应用版本,然后启动一个微型加载器来检测哪个profile该被选择,然后去下载该profile需要的代码文件。
While the universal build approachdoes mean you're downloading code you don't need on every device, for the vastmajority of apps this amounts to so little additional size it's difficult todetect the difference. For very large apps the difference will become morenoticeable so it's possible we'll revisit this subject after 2.0.
的确这种通用架构的程序会在每个设备上都下载一些根本用不到的代码文件,不过对于绝大多数应用程序来说,你多下载的这点文件影响实在是微乎其微。而对于比较庞大的应用程序来说,这个问题可能更值得注意,所以我们可能在2.0的后续版本对它进行处理。
NestedDependencies
级联依赖
For larger apps it's common to splitthe models, views and controllers into subfolders so keep the projectorganized. This is especially true of views - it's not unheard of for largeapps to have over a hundred separate view classes so organizing them intofolders can make maintenance much simpler.
大一些应用通常会把数据模型、视图、控制器分别存储在不同子文件夹下,这样可以让整个项目看起来更清晰明了一些。对于视图来说尤其如此,大型应用拥有上百个独立的视图类并非天方夜谭,因此分文件夹存储几乎不可避免。
To specifydependencies in subfolders just use a period (".") to specify thefolder:
指定子文件夹中的依赖项只需要使用“.”来分割文件夹路径即可:
Ext.application({
name: 'MyApp',
controllers: ['Users','nested.MyController'],
views: ['products.Show','products.Edit', 'user.Login']
});
In this case these 5 files will beloaded:
上例中将会加载下列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 matchwithin each configuration here - for each model, view, controller, profile orstore you can specify either just the final part of the class name (if you followthe directory conventions), or the full class name.
我们可以混合使用两种方式来定义每个数据模型、视图、控制器、配置文件和存储器:快捷路径方式(符合mvc推荐原则的类只写最后的类名即可)和完整路径方式(自定义路径的类则写完整路径加类名)。
ExternalDependencies
外部依赖项
We can specify applicationdependencies from outside our application by fully-qualifying the classes wewant to load. A common use case for this is sharing authentication logicbetween multiple applications. Perhaps you have several apps that login via acommon user database and you want to share that code between them. An easy wayto do this is to create a folder alongside your app folder and then add itscontents as dependencies for your app.
我们可以通过指定想要加载的完整类名方式来定义应用程序之外的类作为依赖项。这种情况最常见的用途就是在多个应用之间共享认证逻辑。我们可能会有好几个应用程序都要到同一个用户数据库进行验证并实现登录,这时我们当然希望它们能够共享用户登录的代码。比较容易的方式就是在应用程序文件夹之外创建一个单独的文件夹然后把其中的内容作为依赖项添加到应用程序中去。
For example, let's say our sharedlogin code contains a login controller, a user model and a login form view. Wewant 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 fromoutside our application, the last two from the application itself. Note how wecan still mix and match application files and external dependency files.
前面三个文件加载自应用程序外部,后两个则来自应用程序内部。同样我们可以混合调用内外部依赖项。
Note that to enable the loading ofexternal dependencies we just have to tell the Loader where to find thosefiles, which is what we do with the Ext.Loader.setPath call above. In this casewe're telling the Loader to find any class starting with the 'Auth' namespaceinside our 'Auth' folder. This means we can drop our common Auth code into ourapplication alongside the app folder and the framework will be able to figureout how to load everything.
想要启用外部依赖项加载,我们只需告诉Loader到哪里可以找到这些文件即可,Ext.Loader.setPath就是干这个的。上例中我们告诉Loader所有以Auth命名空间中的文件都可以到Auth这个文件夹中找到。这样我们就能把应用程序文件夹之外的通用验证代码都拽进来了,其他的事情由ST框架来处理。
WhereEach Dependency Belongs
依赖项应该放在哪里
The general rulewhen deciding where to declare each dependency is to keep your classescompletely self-contained. For example, if you have a view that containsseveral other views, you should declare those dependencies inside the viewclass, 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 declarethose dependencies for two reasons - it keeps your app.js clean and enables youto reliably require your MyApp.view.Main and know that it already has all ofits dependencies satisfied. The alternative would be to list all of your viewsinside 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 thisis that app.js just contains top-level views. If you useExt.create('MyApp.view.SomeView') inside your app, that view can be consideredtop-level. If a view is only ever constructed as a sub-view of another view (aswith MyApp.view.Navigation and MyApp.view.MainList above), it doesn't belong inapp.js.
换种方式来描述这个问题,app.js只需要包含最顶级的视图即可。你在应用程序内部通过Ext.create('MyApp.view.SomeView')来创建的视图就可以视作顶级视图。其他那些仅仅被作为某些视图内部子视图的(比如例子中的MyApp.view.Navigation和MyApp.view.MainList)就不应该出现在app.js里面。
Changessince 1.x
跟1.x版本的不同之处
In Sencha Touch 1, dependencies wereoften specified in Controllers as well as in the Ext.application call. Whilethis offered some conveniences, it also masked the true architecture of thesystem and coupled views, models and stores too closely to controllers. Here'swhat you could do in 1.x:
在ST1中,依赖项经常在控制器中指定(就跟Ext.application中那样)。这样的确提供了一些便利,但同时也模糊了系统的真实架构,而且视图、数据模型和数据存储器与控制器之间靠得太紧密。在1.x中我们是这么做的:
//1.x code,deprecated
Ext.regController('SomeController',{
views: ['Login'],
models: ['User'],
stores: ['Products']