翻译 - EXT JS 5:Controlling an Application with Router
最近做管理后台苦于前端样式,遂想学习在Ext,继而从新发布的5下手,英语水平有限,且翻且纠结着,欢迎指正。
原文:http://docs.sencha.com/extjs/5.0.0/application_architecture/router.html
使用路由来控制一个应用
在常规网站上,用户通过点击链接或提交表单导航到不同的页面。然而,在单页应用中,用户的交互不会载入新的页面。它在单个页面内处理,使用组件对交互做出反应。那么如何让用户依然能使用他们浏览器的前进后对按钮呢?答案就是使用Ext JS 5的新路由类针对URI的hash值做出响应。
路由可以做什么
路由可以使用浏览器的历史记录来追踪应用的状态。路由还可以允许链接到应用中的子应用。
路由不可以做什么
路由不应用来记录任何数据和会话,数据应该存在如cookie或localstorage这样的持久化数据源中。路由仅仅意味着用来追踪应用的状态。
什么是Hash
浏览器导航使用的URI有多部分组成,看下面这个例子
http://www.example.com/apps/users#user/1234
这应该很常见,然而你可能没有注意到#user/1234。URI的这部分叫做Hash或片段标识符。关于hash的更多信息,请参见:http://en.wikipedia.org/wiki/Fragment_identifier。hash为应用提供了一种不用刷新页面就可以控制浏览器历史记录的方式。如果hash值变了,浏览器会把整个URI添加到浏览器的历史堆栈中,这样就可以使用浏览的前进/后退按钮在包含了可能改变了hash的URIs之间切换。例如,你这样改变下hash看看会发生什么:
http://www.example.com/apps/users#user/5678
浏览器触发了一个hashchange事件,我们可以在应用是利用这点。用户可以点击后退按钮回到hash为#user/1234的页面,这意味着你可以在应用中针对这个改变做出响应。很重要一点值得注意,hash值不会被发送到服务器端。hash值通常由客户端来处理。Ext的路由及时利用浏览器的hash功能来追踪应用状态和深层链接(不知如何表达的更好理解些)。
在应用中实现路由
路由类是Ext JS 5中新加入的,使得在MVC应用中对与hash改变的处理变得简单。我们一直有Ext.util.Histroy类,你可以用它来对hash的改变做出响应。然而,Ext.util.History提供了许多手动的处理方式,一个合理的Router类正式我们所需要的。通过在视图控制器中定义路由,可以很方便的将Router类集成到一个Ext JS 5的MVC应用中。一个路由就是和hash匹配的字符串,且允许在一个Ext应用中深层链接。下面是一个在控制器中实现的基础示例:
Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', routers: { 'users': 'onUsers' }, onUsers: function() { // ... } });
路由将会根据#users hash值执行控制器中的onUsers方法。正如你所看到的,路由是在config对象中配置而不是在顶层配置。
更新Hash
为了更新hash值,控制器提供了一个redirectTo方法。
this.redirectTo('user/1234');
hash会被改变为"#user/1234"且路由中对应的配置项将会被执行。很多情况下可能更新的hash和现有的是意一样的,在这种情况下,redirectTo将返回false且路由配置不会被执行。redirectTo可以接收第二参数,如果设置为true,路由会被强制做出响应(相当于刷新当前页)。
this.redirectTo('user/1234', true);
即便当前的hash与redirectTo处理的hash匹配,路由类也会执行匹配的路由。
缺省令牌
当启动一个应用,如果没有提供hash你也许想要设置一个默认值。举例说明,应用在hash为#home的时候展示一个仪表盘,当没有hash存在的时候,你也许想要(自动)加上#home。为了让缺省的hash奏效,你需要在应用中的/app/view/Application.js文件中配置defaultToken配置项,如下:
Ext.define('MyApp.Application', { extend: 'Ext.app.Application', // ... defaultToken: 'home' });
当应用启动时,它会检查当前URI中的hash,如果存在就执行响应的路由配置,如果不存在,会加上#home,然后再执行对应的配置项。
带有参数的hash值
一个应用还可以在hash中标识出参数。你想要把一个用户ID参数包含进hash中就是个例子。前面我们我们曾把#user/1234作为hash。在这种情况下1234作为一个ID参数。你需要在控制器设置来处理#user/1234:
Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', routes: { 'user/:id': 'onUser' }, onUser: function(id) { // ... } });
我们将'user/:id'作为路由,冒号标识后面是个参数且在应用的onUser方法中将(URI中的)参数作为(函数)参数来处理。对于包含多个参数的情况,控制器方法将按照路由中指定的顺序和数量接收参数。
Hash参数格式化
应用需要想要限制用户ID为指定的格式。ID是个数字我们都知道了,你也许还想使用conditions配置项让路由配置为一个
Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', routes: { 'user/:id': { action: 'onUser', condtitions: { ':id': '([0-9]+)' } }, onUser: function(id) { // ... } });
我们来逐步分析下这个例子。首先,'onUser'方法移到了action配置中。这和我们使用字符串配置路由是一样的。接着我们是用conditions配置了一个对象。键是我们想要控制的带有冒号的参数名,还提供了一个正则表达式字符串(不是正则表达式对象)。对于:id的条件,我们使用([0-9]+),只允许匹配任意多个0到9之间的数字。我们使用字符串是应为正则对象会匹配全部的hash,所以如果有多个参数在路由中我们需要将正则表达式字符串合并到一起放入一个正则对象中。如果你没有为参数提供条件,默认的为:
([%a-zA-Z0-9\\-\\_\\s,]+)
处理路由
很多时候应用需要对路由做预处理。这中情况,我们想要检查管理权限查看用户是否有权限访问应用的部分功能。可以在路由中配置一个可以让当前或所有路由停止或继续执行的预处理动作。
下面是一个继续执行路由的例子:
Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', routes: { 'user/:id': { before: 'onBeforeUser', action: 'onUser' } }, onBeforeUser: function(id, action) { Ext.Ajax.request({ url: '/secerity/user/' + id, success: function() { action.resume(); } }); }, onUser: function(id) { // ... } });
在onBeforeUser方法中,:id参数作为一个参数被处理,但是最后一个参数是action。如果action执行resume方法,路由就会继续执行,onUser将会被调用。注意早路由被继续执行前,我们可以等待一个AJAX请求完成。
我们来展开这个例子,通过调用action.stop()来告诉应用停止继续执行路由。
Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', routes: { 'user/:id': { before: 'onBeforeUser', action: 'onUser' } }, onBeforeUser: function(id, action) { Ext.Ajax.request({ url: '/security/user/' + id, success: function() { action.resume(); }, failure: function() { action.stop(); } }); }, onUser: function(id) { //... } });
Ext JS应用可以复合,也许会有多个控制器同时监听一个路由的情况。然而,只有一个设置了预处理动作的控制启动,既只有一个Ajax请求触发。如果给action的stop方法传递了true参数,后续的路由也将被停止,并不是只有当前的路由被停止执行。
Ext.define('MyApp.view.main.MainController', { extend : 'Ext.app.ViewController', routes : { 'user/:id' : { before : 'onBeforeUser', action : 'onUser' } }, onBeforeUser : function(id, action) { Ext.Ajax.request({ url : '/security/user/' + id, success : function() { action.resume(); }, failure : function() { action.stop(true); } }); }, onUser : function(id) { //... } });
现在,如果ajax请求失败,我们通过传递true来取消这条路由在所有控制器中所有的处理。
注意:如果没有执行resume或stop方法,路由会终止且永远不会合理的完成。有些时候执行其中的一个相当重要。
处理没有匹配上的路由
如果hash变了,但没有匹配上,路由将什么动作都不做;路由类不会改变hash,而是任之不理。路由类会在监听了unmatchedroute事件的Ext.applicaiton调用中,在应用实例上出发unmatchedroute事件:
Ext.application({ name : 'MyApp', listen : { controller : { '#' : { unmatchedroute : 'onUnmatchedRoute' } } }, onUnmatchedRoute : function(hash) { //... } });
在一个hash中使用多个路由
只从Ext JS应用可以复合(复杂的?),很多时候需要在一个hash中使用多个路由。路由类设置为处理这个,所以在控制器中没有额外路由配置到控制器中(这句实在理不通,求教)。相反你可以使用竖线|分割hash。如:
`#user/1234|messages`
在这种情况下,我们想要展示id为1234的用户详情,又想展示信息。对应的路由将按照hash中的顺序都会被执行。他们有各自的沙盒(互不干扰)。简而言之,如果你停止了user/1234的路由,messages的照样执行。切记路由执行的顺序和hash中设置的值一致的,也就是说在上面这个例子中user/1234一直都会在messages之前被处理。
也许你想修改分隔符,可以通过修改Ext.app.route.Router.multipleToken属性类实现。
last updated June 4, 2014