director.js 前端 Hash 路由表框架
director.js 是一个客户端的路由注册/解析器,它在不刷新页面的情况下,利用 # 符号组织不同的 URL 路径,并根据不同的 URL 路径来匹配不同的回调方法。通俗点说就是什么样的路径干什么样的事情。
director.js 适用于客户端浏览器以及 node.js 的服务器端应用,在两者的应用场景里调用方法仅有少量的不同。它非常适合用来开发不需要刷新的单页程序以及 node.js 应用。尤其单页程序,对于不想介入手机 APP 开发的同学,可以利用这个实现类似 APP 的应用程序。
director.js 不依赖于任何其他的 js 库,例如 jquery 等。但是它又和 jQuery 能很好的融合在一起进行使用。
客户端的路由
客户端的路由 (也称为哈希路由) 允许您指定一些关于使用URL应用状态的信息,当用户指定固定的URL,进行相应的页面显示。
简单示例
上文简单提到: director.js 是通过 # 符号进行路径组织的,例如:
http://www.demo.com/#/show http://www.demo.com/#/show/list http://www.demo.com/#/show/single
路由注册在URL里的体现是用 # 符号来标识路由的开始,再利用 / 分隔符(分隔符可自定义,后面会讲到)来定义路由片段。客户端路由其实就是通过URL来区分应用程序的不同状态,并且定义在不同的状态下应该做什么事情。当用户访问不同的 URL 时,director.js 会解析路由信息并告知应用程序需要做什么事情。
路由格式具体可参考下图:
下面是一个简单的例子:
<html> <head> <title>A Gentle Introduction</title> <script src="/js/director.min.js"></script> <script> var author = function () { console.log("author"); }, books = function () { console.log("books"); }, viewBook = function(bookId) { console.log("viewBook: bookId is populated: " + bookId); }; var routes = { '/author': author, '/books': [books, function() { console.log("An inline route handler."); }], '/books/view/:bookId': viewBook }; var router = Router(routes); router.init(); </script> </head> <body> <ul> <li><a href="#/author">#/author</a></li> <li><a href="#/books">#/books</a></li> <li><a href="#/books/view/1">#/books/view/1</a></li> </ul> </body> </html>
和 jQuery 一起使用的列子:
<html> <head> <meta charset="utf-8"> <title>A Gentle Introduction 2</title> <script src="/js/jquery.min.js"></script> <script src="/js/director.min.js"></script> <script> $('document').ready(function(){ var showAuthorInfo = function () { console.log("showAuthorInfo"); }, listBooks = function () { console.log("listBooks"); }, allroutes = function() { var route = window.location.hash.slice(2), sections = $('section'), section; if ((section = sections.filter('[data-route=' + route + ']')).length) { sections.hide(250); section.show(250); } }; var routes = { '/author': showAuthorInfo, '/books': listBooks }; var router = Router(routes); router.configure({ on: allroutes }); router.init(); }); </script> </head> <body> <section data-route="author">Author Name</section> <section data-route="books">Book1, Book2, Book3</section> <ul> <li><a href="#/author">#/author</a></li> <li><a href="#/books">#/books</a></li> </ul> </body> </html>
View Code
下面将会就 director.js 客户端相关的 API 进行详细的说明。
初始化及路由注册
director.js 的主要对象是 Router 对象,构造方法如下:
var router = new Router(routes);
构造方法中传入的 routes 参数是一个路由表对象,它是一个具有键值对结构的对象,路由允许多层的嵌套定义。
键值对的键对应URL中传入的路径,一般一个键对应按分隔符切割后的某一部分;而键值对的值则对应该路径需要触发的回调函数名,可以传入一个或多个函数名,传入多个函数名时请使用数组对象。一般来说,回调函数要在路由表对象使用前先声明,否则js会报错。
另外,回调函数除非特殊情况,一般不推荐使用匿名函数,请尽量先声明后使用。
var routes = { '/dog': bark, '/cat': [meow, scratch] };
上面例子中,对应的URL分别为:#/dog 和 #/cat
声明Router对象后,需要调用init()方法进行初始化,如下:
var routes = { '/dog': bark, '/cat': [meow, scratch] }; var router = Router(routes); router.init();
路由的即时注册
当我们在开发一些规模比较大的应用的时候,一般做不到一开始就将需要的路径和它对应的回调函数都预先准备好。很多时候,我们都是在做到某一功能时,或者是开发一些独立性比较强耦合度比较低的模块时,才知道我们需要什么样的路径和回调函数。这个时候我们就需要实时注册路由的功能了。
director.js 通过 on 方法,提供对即时注册功能的支持,示例如下:
var router = Router(routes).init(); .... router.on('/rabbit', function(){ ... })
路由事件
路由事件是路由注册表中一个有固定命名的属性,是指当路由方法router.dispatch()被调用时,路由匹配成功的时定义的需要触发的回调方法(允许定义多个回调方法)。上文即时注册功能里的"on"方法就是一个事件。具体信息如下:
- on :当路由匹配成功后,需要执行的方法
- before:在触发 on 方法之前执行的方法
仅在客户端有效的方法:
- after:当离开当前注册路径时,需要执行的方法
- once: 当前注册路径仅执行一次的方法
var routes = { "/about/:id": { before: function (id) { alert("direct to : /Home/About/" + id); }, on: function (id) { window.location = "/Home/About/" + id; } };
*以上示例中使用了匿名函数,仅因为做为示例比较方便,不推荐这种使用方式
配置参数
director.js 通过配置一些可选项的参数从而提升Router对象的灵活性。而这些参数的设置需要通过router.configure()方法实现。
var router = new director.Router(routes).configure(options);
具体的配置参数有:
- recurse:控制路由递归触发方式的参数,可选值为"forward","backward"和"false",客户端的默认值是"false",而服务端的默认值是"backward"
- strict:当值为"false"时,路径允许以"/"结尾(也可以是其他自定义的分隔符);默认值是"true",说明默认不允许路径以"/"结尾
- async:同步异步控制器,值为"ture","false",默认值为"false"
- delimiter:路由分隔符,默认值为"/"
- notfound:当路由方法router.dispatch()被调用时,没有匹配到任何路由时触发的方法
- on:当路由方法router.dispatch()被调用时,任何一个路由匹配成功后都需要执行的方法;与上文路由事件中的 on 事件的区别类似于全局和局部的概念,路由表中仅针对当前注册的路由;而configure方法中的"on"则针对全局的所有路由
- before:当路由方法router.dispatch()被调用时,当任何一个路由匹配成功并在"on"执行之前需要执行的方法;与上文路由事件中的 before 事件的区别同上
仅在客户端有效的参数:
- resource:用来进行回调函数绑定的基于字符串的对象。使用该参数能实现回调函数的延迟绑定(原词是 "late-binding",后面有相关的详细说明)
- after:当给定的路径不再是当前激活的路径时触发的方法,可以理解为离开当前路径后触发的方法;与上文路由事件中的 after 事件的区别同上
*以上的配置参数在后面均有详细的讲解
*另外仅在客户端起效的参数还有html5history和run_handler_in_init,这两个参数是与html5相关,暂时也不讨论
URL匹配
在路由事件那一节的示例里,有这么一个路由表达式"/about/:id",其中":"后面定义的部分表示实际路径对应的这部分是传入回调函数的参数,例如"#/about/5"中,5就是id参数的值。参数的匹配还可以用嵌套的方式来定义:
var router = Router({ '/about': { '/:id': { on: function (id) { console.log(id) } } } });
在实际应用的过程中,我们的路由可能会变得非常复杂,像"/about/:id"这样简单的表达式并不能满足我们的需求。而director.js 支持利用正则表达式来匹配复杂的路由名称,匹配到的值会作为参数传给回调函数,例如:
var router = Router({ '/hello': { '/(\\w+)': { on: function (who) { console.log(who) } } } });
当URL传入'#/hello/world',则回调函数的who=world
支持更为复杂的多参数的传递:
var router = Router({ '/hello': { '/world/?([^\/]*)\/([^\/]*)/?': function (a, b) { console.log(a, b); } } });
当URL传入'#/hello/world/johny/appleseed',则回调函数的a=johny,b=applesee
路由的递归匹配
全局配置中的recurse参数决定了路径在路由表中的命中方式以及命中顺序。命中方式包括递归命中和精确命中;命中顺序包括正序,反序和中断。
当参数设定为 forward、backward 时,表明路由的命中方式是递归命中,并按照规定的顺序命中,forward为正序,backward为反序。路由如果定义了多个路由片段,则它们对应的回调函数均可命中,也就是说一个路径可以命中多个函数。
当没有特别指定该参数的值时(即默认值"false"),则标明路由的命中方式为精确命中,需要完全匹配整个路径后才可以命中,也就是说一个路径只能命中一个函数。
当URL传入"#/dog/angry"时,路由表注册如下:
var routes = { '/dog': { '/angry': { on: growl }, on: bark } };
当没有指定递归匹配的方式时,仅命中 growl 方法。
当指定递归匹配参数的值为 backward 时,首先命中 growl 方法,然后再命中 bark 方法,按照路径的注册顺序反序命中。如下:
var router = Router(routes).configure({ recurse: 'backward' });
当指定递归匹配参数的值为 forward 时,首先命中 bark 方法,然后再命中 growl 方法,按照路径的注册顺序正序命中。如下:
var router = Router(routes).configure({ recurse: 'forward' });
当指定了递归匹配参数后,如果命中的函数中使用了 return false 的语句返回,则会中断递归命中,直接返回。如下:
var routes = { '/dog': { '/angry': { on: function() { return false; } }, on: bark } }; var router = Router(routes).configure({ recurse: 'backward' });
无中断时,首先会命中 angry 下的方法,然后命中 bark 方法;但是在使用return false 语句之后,执行完 angry 命中的方法后将不再递归命中别的方法(bark)。
资源参数
全局配置中的 resource 参数仅在客户端应用中可用,它是一个文本对象,其中的文本属性值主要用来定义路由匹配命中后的回调方法名。它可以为程序提供更好的封装性,以便更好的进行结构设计。
var router = Router({ '/hello': { '/usa': 'americas', '/china': 'asia' } }).configure({ resource: container }).init(); var container = { americas: function() { return true; }, china: function() { return true; } };
示例中 container 对象定义在路由对象之后,我觉得这个就是之前说的 延迟绑定 功能。因为路由的回调函数绑定必须遵守先声明后使用的方法,这个示例明显是先使用后声明。但是我测试了一下,这样做会报错,还是不能改变先声明后使用的顺序。因此延迟绑定的意思我还没真正搞清楚。
github 地址:https://github.com/flatiron/director
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论