SPA学习之 - 路由插件(crossroads.js)
Crossroads.js是一个受Rails, Pyramid, Django, CakePHP等基于路由/分发(Route/Dispatch)方式处理路由的后端MVC框架启发的一套js专业路由库。它能够直接解析传入的字符串并根据相应的规则来过滤和验证路由,然后再执行下一步的操作。
A duck can walk, fly and swim, but he can’t do any of these things well…
crossroads.js是一个独立的库功能十分强大和灵活,而且职责单一、目标定位清晰。如果在项目中应用适当不尽可以减少代码的复杂度,同时也可以通过在页面中使用hash路由方式来抽象页面路径和服务器请求。当然在开发SPA页面过程中,如果我们不使用backbone, Angular等类似的MVC库来开发SPA的话,它是一个相当不错的选择。当然还有一个router.js也是一个相当不错的独立路由插件,后续我会专门介绍其用法。类似的一些处理路由的js库还有:urianchor(在《单页Web应用:JavaScript前端到后端》这本书中有介绍其使用方法)、jquery-hashchange、pathjs等。
crossroads.js由Miller Medeiros开发, 它的使用不依赖于第三方库,内部仅仅只依赖于作者另一个js库signals.js:将ActionScript一个开源库as3-signals移植到了js。
js信号机制可以参考阮一峰老师的日志”Javascript异步编程的4种方法“。
由于crossroads.js只定义了路由配置和规范,当页面监有路由请求信号发出时crossroads.js会监听到信号,同时浏览器地址栏中的地址栏也会指向目标地址,但还需要一个处理页面hashchange
事件的库。这里可以使用作者开发的库hasher.js或者使用history.js来处理’hashchange’事件。
下面我将和大家一起来了解crossroads.js的用法:
一、基本使用方法
首先,先来看几个常见的URL地址,以点名时间的路由为例:
// 新品首页 http://www.demohour.com/projects/latest // 新品筛选 http://www.demohour.com/projects/latest?attribute=online&category=927151&sort=2 // 项目详情页面 http://www.demohour.com/projects/354263
点名时间
产品分为’新品’、’特卖’和’预售’,那么对应的路由定义可以这样:
// 这里`projectsType`值为: ‘latest‘, ‘sell‘, ‘presell‘ /projects/{projectsType}
相应的新品筛选带参地址定义如下:
/projects/latest?attribute={attributeValue}&category={projectsId}&sort={sortord}
地址中对应参数值定义如下:
// 分类 attributeValue = { ‘927219‘: ‘通讯数码‘, ‘927151‘: ‘家居生活‘, ‘927158‘: ‘智能穿戴‘, ‘927218‘: ‘影音娱乐‘, ‘927162‘: ‘出行定位‘, ‘927250‘: ‘办公相关‘, ‘927220‘: ‘其它‘ }; // 排序 sortord = { ‘0‘: ‘默认排序‘, ‘1‘: ‘评分最高‘, ‘2‘: ‘评价最多‘, ‘3‘: ‘收藏最多‘, ‘4‘: ‘浏览最多‘ }
那么我们如何在SPA页面中通过crossroads.js实现以上路由的定义呢? 首先引入相应的js库:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SPA学习之 - 路由插件(crossroads.js)</title> <script src="js/jquery-1.11.2.min.js"></script> <script src="js/signals.min.js"></script> <script src="js/hasher.min.js"></script> <script src="js/crossroads.min.js"></script> </head> <body> <h1>SPA学习之 - 路由插件(crossroads.js)</h1> <script> $(function() { // 这里输入内容 }); </script> </body> </html>
在crossroads.js中定义一个路由可以使用crossroads.addRoute(pattern, [handler], [priority]):Route
方法,可以接受三个参数:
- parttern: 对应路由规则,可以为字符串或者正则表达式
- handler: 可选参数,当监听到路由时的处理方法
- priority: 可选参数,定义路由在路由队列中的优先级
从函数的定义中可以看出其返回一个Route
对象,后面会讲解Route
对象相关属性和方法。在crossroads.js中我们可以通过以下两种方式定义一个路由:
// 方式一:定义路由时同时定义操作 crossroads.addRoute(‘projects/{projectsType}‘, function(projectsType) { console.log(projectsType); }); // 方式二:只定义一个路由,返回一个`Route`对象 var projectsRoute = crossroads.addRoute(‘projects/{projectsType}‘); // 这里通过`Route.matched`信号来处理操作 projectsRoute.matched.add(function(projectsType) { console.log(projectsType); }); // 通过`crossroads.parse()`方法来解析一个路由 crossroads.parse(‘/projects/sell‘); // 输出: sell
在上面分析 点名时间
路由时,我们看到带查询参数的新品筛选地址, 在crossroads.js中可以通过对每个分组名前添加”?
“(例如:{?foo}、:?bar:)来实现。
crossroads.addRoute(‘projects/{projectsType}/{?filter}‘, function(projectsType, filter) { console.log("项目类型:" + projectsType + ‘;\n‘ + "排序:" + filter.sort + ‘;\n‘ + "项目ID:" + filter.category + ‘;\n‘ + "分类:" + filter.attribute + ‘;‘); }); crossroads.parse(‘projects/latest?attribute=online&category=927151&sort=2‘); /* * 输出结果 * ================ * 项目类型:latest; * 排序:2; * 项目ID:927151; * 分类:online; * ================ */
解析后所有匹配的值会被转换到一个对象中,默认情况下crossroads.js不会关心crossroads.parse()
中传入的数据类型,传入的数字也会当成字符串处理。如果设置crossroads.shouldTypecast = ture
, 那么会对处理后的对象值做出相应的类型判断。
crossroads.addRoute(‘projects/{projectsType}‘, function(projectsType) { console.log(projectsType); console.log(typeof projectsType); }); crossroads.parse(‘/projects/sell‘); // 输出: sell和string crossroads.parse(‘/projects/12323‘); // 输出: 12323和string // 将对象类型检查开启 crossroads.shouldTypecast = true; crossroads.parse(‘/projects/sell‘); // 输出: sell和string crossroads.parse(‘/projects/12323‘); // 输出: 12323和number
在定义路由表达式时也可以通过用”::
“包含一个字符或正则来实现可选路由(optional segments):
crossroads.addRoute(‘foo/:bar:/:far:‘, function(bar, far) { console.log(bar + " " + far); }); crossroads.parse(‘foo‘); // undefined undefined crossroads.parse(‘foo/abc‘); // abc undefined crossroads.parse(‘foo/abc/xyz‘); // abc xyz
如果需要更多的可选路由(在其官网中称为:’rest’ segments),而且长度不确定,那么可以通过在分组尾部以’*’结尾来实现。
crossroads.addRoute(‘foo/:bar*:‘, function(bar) { console.log(bar); }); crossroads.parse(‘foo‘); // undefined crossroads.parse(‘foo/abc‘); // abc crossroads.parse(‘foo/abc/xyz‘); // abc/xyz crossroads.parse(‘foo/abc/xyz/123‘); // abc/xyz/123
在路由解析过程中,如果同一个地址能够被多个规则解析,那么可以通过指定crossroads.addRoute()第三个参数来提升优先级
/* 默认情况下按照声明先后顺序解析 */ crossroads.addRoute("bar/{lorem}", function(lorem) { console.log(lorem); }); crossroads.addRoute("bar/{lorem}/:date:", function(lorem, date) { console.log(lorem + ‘ : ‘ + date); }); crossroads.parse("bar/1"); // 1 crossroads.parse("bar/abc"); // abc crossroads.parse("bar/abc/2015-3-11"); // abc : 2015-3-11 /* 下面通过设置优先级来改变默认解析顺序 */ // 默认情况下优先级为`0` crossroads.addRoute("bar/{lorem}", function(lorem) { console.log(lorem); }, 0); // 指定第二个路由优先级为`1` crossroads.addRoute("bar/{lorem}/:date:", function(lorem, date) { console.log(lorem + ‘ : ‘ + date); }, 1); crossroads.parse("bar/1"); // 1 : undefined crossroads.parse("bar/abc"); // abc : undefined crossroads.parse("bar/abc/2015-3-11"); // abc : 2015-3-11
crossroads.js既然能够创建路由,当然也有移除路由对应的方法:crossroads.removeRoute(route)
以及crossroads.removeAllRoutes()
:
// 路由一 var a = crossroads.addRoute("foo/{bar}", function(bar) { console.log("第一个路由:" + bar); }); // 路由二 crossroads.addRoute("bar/{lorem}", function(lorem) { console.log("第二个路由:" + lorem); }); // 路由三 crossroads.addRoute("lorem/{ipsum}", function(ipsum) { console.log("第三个路由:" + ipsum); }); crossroads.parse(‘foo/route1‘); // 第一个路由:route1 crossroads.parse(‘bar/route2‘); // 第二个路由:route2 crossroads.parse(‘lorem/route3‘); // 第三个路由:route3 console.log(crossroads.getNumRoutes()); // 3 // 移除路由一 crossroads.removeRoute(a); // 第一个路由已从路由队列中移除了,不再接受解析 crossroads.parse(‘foo/route4‘); crossroads.parse(‘bar/route5‘); // 第二个路由:route5 crossroads.parse(‘lorem/route6‘); // 第三个路由:route6 console.log(crossroads.getNumRoutes()); // 2 // 移除所有路由 crossroads.removeAllRoutes(); // 下面三个解析都没有任何作用,因为当前没有可解析的路由 crossroads.parse(‘foo/route7‘); crossroads.parse(‘bar/route8‘); crossroads.parse(‘lorem/route9‘); console.log(crossroads.getNumRoutes()); // 0
注意在上面示例中,由于crossroads.removeRoute()
需要一个Route
对象作为参数,所以最好以第二种方式来定义一个路由规则,同时也方便路由统一配置和管理。crossroads.getNumRoutes()
方法用于获取当前路由队列中路由数量。
未完待续。。。