使用路由-Controlling an Application with Router
对于一个常规网站,用户通过点击链接或者填写表单等在不同的页面间跳转。但是,对于一个单页面应用来说,用户的一次交互不会加载新的页面。相反,是在一个页面内进行处理,由组件对用户的交互做出响应。那么如何还能让用户继续使用浏览器的前进和后退按钮呢?答案就是通过 Ext JS 的 Router
来处理 URI 的 hash 变更。
用路由做什么-What Routing Is Used For
路由通过利用浏览器的历史记录,可以用来跟踪应用程序的状态。路由还可以深入链接到应用程序的某个特定部分。
不用路由做什么-What Routing Is Not Used For
不应该使用路由存储任何数据或会话,数据应该持久化存储在数据源中,例如: cookie
、localstorage
。路由只是跟踪应用程序状态的一种方式。
什么是Hash-What is the Hash?
浏览器使用 URI 进行导航,URI 由许多部分组成。一个简单的 URI 如下:
http://www.example.com/apps/users#user/1234
这个相对熟悉。但是,你可能不能识别 #user=1234
。 URI 的这部分信息称为 hash
或者片段标识(fragment identifier
)。hash 提供了一种方式,不用重新加载当前页面就可以控制浏览器的历史记录。一旦 hash 变了,浏览器将整个 URI 添加到历史记录中,这样就可以使用浏览的前进或者后退按钮进行导航了。例如,如果改变了 hash 的值,会发生什么:
http://www.example.com/apps/users#user/5678
浏览器触发一个 hashchange
事件,我们可以在应用程序中利用这个事件。
如果用户点击后退按钮,就会回到第一个示例的地址 #user=1234
。这个事件通知允许在应用程序中对变更作出响应。需要注意一点,hash 不发送到服务器端,只是在客户端扮演 URI 的作用。 Ext JS Router
依靠浏览器的 hash 功能实现应用程序的状态跟踪和深度链接。
实现路由-Implement Routing in your Application
Router 类是 Ext JS 5 新增的功能,使得在 MVC 应用程序中,很容易处理 hash 的变更、解释和处理。还有 Ext.util.History
类,也可以用于处理 hash 变更,但是它提供更多的手动处理,并且还需要一个 Router 类。 Router 类可以通过在视图控制器中定义的路由,很容易与 MVC 应用集成在一起。一个路由是一个与 hash 对应的字符串,在 Ext 应用中可以进行深度的链接。例如:
Ext.define('MyApp.view.main.MainController', {
extend : 'Ext.app.ViewController',
routes : {
'users' : 'onUsers'
},
onUsers : function() {
//...
}
});
这个路由响应 #users
hash,将在控制器实例的作用域内调用 onUsers
方法。
更新Hash-Updating the Hash
可以利用控制器的 redirectTo
方法更新 Hash:
this.redirectTo('user/1234');
该示例修改 hash 为 #user/1234
,能够识别该 hash 的配置方法将被调用。可能当前的 hash 与要更改的 hash 相同,这时 redirectTo
方法返回 false,并且不会执行对应的规则。 redirectTo
方法还可以接收第二个参数,如果传递 true
可以强制路由响应 hash 值。
this.redirectTo('user/1234', true);
即使当前的 hash 与传递的 hash 相同,Router 也将执行匹配的规则。
缺省路由-Default Token
当应用程序启动时,如果未提供 hash 值,可以配置缺省的 hash 值。例如:当 hash 值为 #home
时显示仪表板功能,可以在没有 hash 值时缺省使用 #home
。 想要使用缺省 hash 值,可以设置 /app/view/Application.js
文件中的 defaultToken
配置属性:
Ext.define('MyApp.Application', {
extend : 'Ext.app.Application',
//...
defaultToken : 'home'
});
路由参数-Hashes with Parameters
应用程序可以在 hash 中定义参数。例如,用户标识可以放置在 hash 值中,之前示例中提到过的 #user/1234
。这种情况下,我们可能希望 1234
作为 id
参数,可以如下设置控制器的路由规则:
Ext.define('MyApp.view.main.MainController', {
extend : 'Ext.app.ViewController',
routes : {
'user/:id' : 'onUser'
},
onUser : function(id) {
//...
}
});
路由设置为 'user/:id'
和冒号 :
。 这表示有一个参数需要传递给目标方法。方法接收参数的数量和顺序同路由中定义的数量和顺序一致。
参数格式化-Hash Parameter Formatting
应用程序可能需要强制约束用户标识(User ID)的格式。例如:我们希望是一个数字。可以使用一个对象来定义路由,并且设置 conditions
配置属性:
Ext.define('MyApp.view.main.MainController', {
extend : 'Ext.app.ViewController',
routes : {
'user/:id' : {
action : 'onUser',
conditions : {
':id' : '([0-9]+)'
}
}
},
onUser : function(id) {
//...
}
});
首先,'onUser'
方法放置在 action
配置属性中,这与之前使用字符串的方式类似。然后,使用对象定义一个 conditions
配置属性,要控制的关键字是冒号加参数名 :id
,值是正则表达式(Regular Expression)字符串(不是正则表达式对象)。例如: 对于 :id
我们使用 ([0-9]+)
。之所以使用正则表达式字符串,是因为正则表达式对象用于匹配整个 hash 值。如果存在多个参数,我们需要将正则表达式字符串连接在一起组成一个正则表达式对象。如果没有为参数指定条件,使用如下缺省值:
([%a-zA-Z0-9\\-\\_\\s,]+)
路由处理-Route Handling
应用程序有时需要阻止路由的处理。例如:我们需要检查用户授权,判断当前用户是否能够查阅指定的程序功能。路由需要配置一个前置的操作,可以终止当前的路由,终止所有路由,或者继续执行路由。例如:
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();
}
});
},
onUser : function(id) {
//...
}
});
在 onBeforeUser
方法中,接收 :id
参数,最后一个参数是 action
。如果调用 action
的 resume
方法,路由将继续执行,调用 onUser
方法。 请注意,可以等待 AJAX 请求处理完成后再继续路由的处理。
扩展一下这个示例,调用 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 应用可能很复杂,可能存在多个控制器监听同一个路由。但是,只有一个控制器可以设置 before
配置属性,所以只有一个 ajax 请求。如果传递 true
给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(true);
}
});
},
onUser : function(id) {
//...
}
});
现在,当 Ajax 请求失败时, 我们传递 true
来终止所有控制器针对此路由的处理。
注意:如果没有执行
resume
或stop
方法,路由将被终止,而且不会正常结束。所以切记要在同一个地方,调用resume
或stop
方法。
处理不匹配的路由-Handling Unmatched Routes
如果 hash 变更了,但是没有找到匹配的路由, Router 将没有任何处理动作,它不会尝试修改 hash,继续保留不匹配的 hash。Router 将触发应用程序实例的 unmatchedroute
事件,可以在 Ext.application
调用时配置该事件的监听:
Ext.application({
name : 'MyApp',
listen : {
controller : {
'#' : {
unmatchedroute : 'onUnmatchedRoute'
}
}
},
onUnmatchedRoute : function(hash) {
//...
}
});
在一个Hash中使用多个路由-Using Multiple Routes in a Single Hash
由于 Ext JS 应用可能非常复杂,有时需要在单一 hash 中使用多个路由。 Router 不需要额外的设置就可以处理。只需要用管道符 |
分隔 hash 值,例如:
`#user/1234|messages`
在这个示例中,我们想要显示 1234
用户的详细信息,同时也显示提示信息。hash 中定义的每个路由都将按照定义的顺序执行。然后它们之间就相互隔离了。换句话说,如果停止 user/1234
路由,messages
路由还将继续执行。要注意路由是按照在 hash 中定义的顺序执行的。上述示例中,user/1234
路由总是在 messages
路由之前执行。
可以通过设置 Ext.app.route.Router
的 multipleToken
属性改变缺省的分隔符。