swayok / page.js
小巧的客户端路由器
This package is auto-updated.
Last update: 2024-09-10 04:55:32 UTC
README
受Express启发的简洁客户端路由器。
page('/', index) page('/user/:user', show) page('/user/:user/edit', edit) page('/user/:user/album', album) page('/user/:user/album/sort', sort) page('*', notfound) page()
或路由声明的安全变体
page.route('/', index) page.route('/user/:user', show) page.route('/user/:user/edit', edit) page.route('/user/:user/album', album) page.route('/user/:user/album/sort', sort) page.route('*', notfound)
此版本将不允许您将除字符串以外的任何内容作为第一个参数传递,也不允许将函数作为第二个参数传递
安装
安装 page.js
有多种方式。使用包管理器
$ npm install page # for browserify
$ component install visionmedia/page.js
$ bower install visionmedia/page.js
运行示例
要运行示例,请按照以下步骤安装开发依赖项并运行示例服务器
$ git clone git://github.com/visionmedia/page.js
$ cd page.js
$ npm install
$ node examples
$ open https://127.0.0.1:4000
目前我们有以下示例
basic
最小应用程序,显示基本路由notfound
与basic
相似,具有单页面 404 支持功能album
显示分页和外部链接profile
简单用户资料query-string
展示如何使用路由器集成插件state
阐述如何使用历史状态来缓存数据server
阐述如何使用 dispatch 选项在服务器上初始化内容chrome
Google Chrome 风格的管理界面transitions
展示在“页面”之间添加转场效果的简单技术partials
使用 hogan.js 在客户端渲染 mustache 部分模板
注意:请记住,这些示例没有使用 jQuery 或类似库,因此示例中的某些部分可能相对冗长,尽管它们与 page.js 没有直接关系。
API
page.route(path, callback[, callback ...])
Failsafe variant of page(path, callback[, callback ...])
定义路由映射 path
到给定的 callback(s)
。每个回调函数接收两个参数,上下文 和 next
。类似于 Express,调用 next 将调用具有给定路径的下一个注册的回调。
page.route('/', user.list) page.route('/user/:id', user.load, user.show) page.route('/user/:id/edit', user.load, user.edit) page.route('*', notfound)
在某些条件下,链接将被忽略,不会进行分发,例如
- 不同源的链接
- 具有
download
属性的链接 - 具有
target
属性的链接 - 具有
rel="external"
属性的链接
page(callback)
这相当于 page('*', callback)
用于通用“中间件”。
page(path)
导航到给定的 path
。
$('.view').click(function(e){ page('/user/12') e.preventDefault() })
page(fromPath, toPath)
设置从一路径到另一路径的跳转。
page.redirect(fromPath, toPath)
等同于 page(fromPath, toPath)
page.redirect(path)
调用 page.redirect 仅以字符串作为第一个参数时,将跳转到另一个路由。等待当前路由推送状态后,将其替换为新路由,使浏览器历史记录保持干净。
page('/default', function(){ // some logic to decide which route to redirect to if(admin) { page.redirect('/admin'); } else { page.redirect('/guest'); } }); page('/default');
page.show(path, state, dispatch, push, customData)
导航到 path
。返回:Context
参数
path
- URLstate
- 历史状态对象dispatch
- 布尔值;默认:true
;当false
时,路由器将不会为此 URL 运行任何处理器,但将创建Context
实例push
- 布尔值;默认:true;当false
时,路由器将不会更改浏览器地址栏中的 URLcustomData
- 对象;存储到Context
中的某些特定数据。有关更多信息,请参阅Context#customData
page.replace(path, state, init, dispatch, customData)
导航到 path
替换浏览器历史记录中的当前 URL。返回:Context
参数
path
- URLstate
- 历史状态对象init
- 布尔值;我不确定,但可能只是一个标志,表示在page.start()
(@swayok) 中分发的初始Context
dispatch
- 布尔值;默认:true
;当false
时,路由器将不会为此 URL 运行任何处理器,但将创建Context
实例customData
- 对象;存储到Context
中的某些特定数据。有关更多信息,请参阅Context#customData
page.back(path, state)
导航到历史记录中的上一页或当历史记录为空时导航到 path
。参数
path
- 当历史记录为空时默认导航到的URL。默认:基础URLstate
- 历史状态对象
page.reload()
重新加载当前URL
page([options])
注册页面的 popstate
/ click
绑定。如果您正在进行选择性绑定,您可能需要传递 { click: false }
来指定此操作。以下选项可用
click
绑定到点击事件 [true]popstate
绑定到 popstate [true]dispatch
执行初始分发 [true]hashbang
在URL前添加#!
[false]decodeURLComponents
从路径组件(查询字符串、路径名、hash)中删除URL编码 [true]
如果您希望从服务器加载初始内容,您可能需要将 dispatch
设置为 false。
page.start([options])
与上面的 page([options])
相同。
page.stop()
解绑 popstate
和 click
处理程序。
page.base([path])
获取或设置基础 path
。例如,如果 page.js 在 /blog/*
中运行,则设置基础路径为 "/blog"。
page.exit(path, callback[, callback ...])
定义一个退出路由映射 path
到给定的 callback(s)
。
退出路由在页面更改时调用,使用来自先前更改的上下文。例如
page('/sidebar', function(ctx, next) { sidebar.open = true next() }) page.exit('/sidebar', function(ctx, next) { sidebar.open = false next() })
page.exit(callback)
相当于 page.exit('*', callback)
。
上下文
路由传递 Context
对象,这些对象可以用来共享状态,例如 ctx.user =
,以及 pushState
API 提供的“state”历史记录 ctx.state
。
Context#save()
使用 replaceState()
保存上下文。例如,这对于在用户按下“后退”时加载的HTML或其他资源的缓存很有用。
Context#handled
如果为 true
,则将上下文标记为已处理,以防止 默认404行为。例如,这对于具有无限数量回调的路由很有用。
Context#canonicalPath
包括“基础”路径(如果有)和查询字符串的路径名(如)/admin/login?foo=bar"。
Context#path
路径名和查询字符串(如)/login?foo=bar"。
Context#querystring
无前导 ?
的查询字符串(如 "foo=bar"),默认为 ""。
Context#pathname
无查询字符串的路径名(如)/login"。
Context#state
pushState
状态对象。
Context#title
pushState
标题。
Context#push
指示是否应更改当前URL。默认:null
示例:您处理了显示模态对话框的请求,但您不希望在浏览器中更改页面地址。为此,您只需在请求处理程序中将 contxt.push = false;
设置即可。
注意:如果 push
参数在 page.show()
或 page.replace()
中为 false
,则 contxt.push = true;
不会强制更改URL。
Context#customData
针对此上下文的一些特定数据。允许您提供有关此上下文的更多信息。
可以通过 page.show() 和 page.replace() 将 customData 对象作为第5个参数传递
它用于 page.reload() 以及默认的 onclick() 和 onpopstate() 事件处理程序
- page.reload() 提供 {is_reload: true} customData
- onclick() 提供 {is_click: true, target: DOM Element} customData
- onpopstate() 提供 {is_history: true} customData
路由
路由器使用与 Express 相同的字符串到正则表达式的转换,因此 ":id"、":id?" 和 "*" 等与您预期的一样工作。
与 Express 相似的另一个方面是传递多个回调的能力。您可以使用此功能来简化嵌套回调,或者简单地抽象组件。
分离关注点
例如,假设您有一个用于 编辑 用户的路由,还有一个用于 查看 用户的路由。在这两种情况下,您都需要加载用户。实现这一目标的一种方法是在这里所示的方式中使用几个回调
page('/user/:user', load, show) page('/user/:user/edit', load, edit)
使用 *
字符,我们可以将其更改为匹配所有以 "/user" 前缀的路由,以实现相同的结果
page('/user/*', load) page('/user/:user', show) page('/user/:user/edit', edit)
同样,*
可以用作所有路由之后的通配符,作为 404 处理器,在所有路由之前、之间等。例如
page('/user/:user', load, show) page('*', function(){ $('body').text('Not found!') })
默认 404 行为
默认情况下,当路由不匹配时,page.js 会调用 page.stop()
来取消绑定自身,然后继续重定向到请求的地址。这意味着您可以在不明确绑定某些链接的情况下,使用 page.js 与多页应用程序 一起。
处理参数和上下文
就像 request
和 response
对象在 Express 中被传递一样,page.js 有一个单一的 "Context" 对象。使用之前的 load
和 show
用户的示例,我们可以将任意属性分配给 ctx
以在回调之间保持状态。
要构建一个将加载用户以供后续路由使用的 load
函数,您需要访问传递的 ":id"。您可以使用 ctx.params.NAME
来这样做,就像 Express 一样
function load(ctx, next){ var id = ctx.params.id }
然后对服务器执行某种操作,将用户分配给 ctx.user
以供其他路由使用。然后调用 next()
来传递控制权到后续匹配的路由(如果有的话)。
function load(ctx, next){ var id = ctx.params.id $.getJSON('/user/' + id + '.json', function(user){ ctx.user = user next() }) }
“show” 函数可能看起来像这样,但是您可以根据需要渲染模板或执行任何操作。请注意,在这里 next()
并未调用,因为这被认为是“终点”,并且直到另一个链接被点击或调用 page(path)
,都不会匹配任何路由。
function show(ctx){ $('body') .empty() .append('<h1>' + ctx.user.name + '<h1>'); }
最后,像这样使用它们
page('/user/:id', load, show)
处理状态
当与 pushState
API 和 page.js 一起工作时,您可以提供可选的状态对象,以便在用户导航历史记录时使用。
例如,如果您有一个照片应用程序,您执行了一个相对昂贵的搜索来填充图像列表,通常当用户在浏览器中点击“后退”时,路由会被调用,并且查询会被再次执行。
一个示例实现可能如下所示
function show(ctx){ $.getJSON('/photos', function(images){ displayImages(images) }) }
您可以使用历史记录的状态对象来缓存此结果或任何其他值。这使得在用户按后退时完全省略查询成为可能,从而提供了更好的体验。
function show(ctx){ if (ctx.state.images) { displayImages(ctx.state.images) } else { $.getJSON('/photos', function(images){ ctx.state.images = images ctx.save() displayImages(images) }) } }
注意:如果状态在第一次滴答(xhr、setTimeout 等)之后更改,则必须使用 ctx.save()
,否则它是可选的,并且状态将在分发后保存。
匹配路径
以下是一些将字符串转换为 RegExp
的示例。
匹配显式路径
page('/about', callback)
匹配需要通过 ctx.params.name
访问的参数
page('/user/:name', callback)
匹配多个参数,例如 /user/tj/edit
或 /user/tj/view
。
page('/user/:name/:operation', callback)
匹配一个可选参数和一个必需参数,现在 /user/tj
将匹配与 /user/tj/show
等相同的路由
page('/user/:name/:operation?', callback)
使用通配符字符 *
来匹配段,可通过 ctx.params[N]
获取,其中 N 是 *
的索引,因为您可以使用多个。例如,以下将匹配 /user/12/edit
、/user/12/albums/2/admin
等。
page('/user/*', loadUser)
命名通配符访问,例如 /file/javascripts/jquery.js
将提供 "/javascripts/jquery.js" 作为 ctx.params.file
page('/file/:file(*)', loadUser)
当然,还有 RegExp
字面量,其中捕获组可通过 ctx.params[N]
获取,其中 N 是捕获组的索引。
page(/^\/commits\/(\d+)\.\.(\d+)/, loadUser)
插件
一个示例插件 examples/query-string/query.js 展示了如何创建插件。它将提供一个解析自 node-querystring 的 ctx.query
对象。
使用 "*" 匹配任何路径以解析查询字符串
page('*', parse) page('/', show) page() function parse(ctx, next) { ctx.query = qs.parse(location.search.slice(1)); next(); } function show(ctx) { if (Object.keys(ctx.query).length) { document .querySelector('pre') .textContent = JSON.stringify(ctx.query, null, 2); } }
可用插件
- querystring:提供由node-querystring解析得到的
ctx.query
对象。 - body-parser:为路由提供由body-parser解析得到的
req.body
对象。 - express-mapper:提供对Express API的直接模仿,以便您可以在不修改的情况下,将控制器代码在客户端和服务器上共享。
请提交拉取请求以添加更多内容到这个列表。
运行测试
在控制台
$ npm install
$ npm test
在浏览器中
$ npm install
$ npm run serve
$ open https://127.0.0.1:3000/
支持IE8+
如果您希望路由在不支持pushState的旧版Internet Explorer中工作,可以使用HTML5-History-API polyfill。
npm install html5-history-api
如何将polyfill与路由一起使用(可选)
如果您的Web应用程序位于嵌套的基础路径中,您需要为HTML5-History-API polyfill指定basepath
。在调用page.base()
之前使用:history.redirect([prefixType], [basepath])
- 如有需要,请提供翻译链接。
prefixType
:[string|null]
- 默认用"/"替换锚点(#)后的字符串。basepath
:[string|null]
- 设置基本路径。参见默认的page.base()
"/"。(注意:在pathname
后需要斜杠)
拉取请求
- 将提交分解为单一目标。
- 一个目标应该是相关但需要解释的代码块。
- 提交应以这种形式进行:what-it-is: how-it-does-it 和或 why-it's-needed 或 what-it-is 对于微小的更改
- 拉取请求和提交应该是代码的指南。
许可协议
(MIT许可协议)
版权所有 (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
特此授予任何获得本软件及其相关文档副本(“软件”)的个人免费使用软件的权利,不受任何限制,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,并允许软件的受供者进行此类操作,前提是遵守以下条件
上述版权声明和本许可声明应包含在软件的副本或实质性部分中。
本软件按“原样”提供,不提供任何形式的质量保证,无论是明示的、暗示的,还是关于适销性、特定用途或非侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论这些责任是由于合同、侵权或其他方式引起的,无论这些责任是否与软件或软件的使用或其他操作有关。