理解路由

理解Web路由

什么是路由

在Web开发过程中,经常会遇到『路由』的概念。那么,到底什么是路由?简单来说,路由就是URL到函数的映射。

router和route的区别

route就是一条路由,它将一个URL路径和一个函数进行映射,例如:

/users        ->  getAllUsers()
/users/count  ->  getUsersCount()

这就是两条路由,当访问/users的时候,会执行getAllUsers()函数;当访问/users/count的时候,会执行getUsersCount()函数。

而router可以理解为一个容器,或者说一种机制,它管理了一组route。简单来说,route只是进行了URL和函数的映射,而在当接收到一个URL之后,去路由映射表中查找相应的函数,这个过程是由router来处理的。一句话概括就是 “The router routes you to a route“。

服务器端路由

对于服务器来说,当接收到客户端发来的HTTP请求,会根据请求的URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有URL的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理,等等。

Express为例,

app.get('/', (req, res) => {
  res.sendFile('index')
})

app.get('/users', (req, res) => {
  db.queryAllUsers()
    .then(data => res.send(data))
})

这里定义了两条路由:

  • 当访问/的时候,会返回index页面
  • 当访问/users的时候,会从数据库中取出所有用户数据并返回

不仅仅是URL在router匹配route的过程中,不仅会根据URL来匹配,还会根据请求的方法来看是否匹配。例如上面的例子,如果通过POST方法来访问/users,就会找不到正确的路由。

客户端路由

对于客户端(通常为浏览器)来说,路由的映射函数通常是进行一些DOM的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。客户端路由最常见的有以下两种实现方案:

  • 基于Hash
  • 基于History API
    (1) 基于Hash
    我们知道,URL中#及其后面的部分为hash。例如:
const url = require('url')
var a = url.parse('http://example.com/a/b/#/foo/bar')
console.log(a.hash)
// => #/foo/bar

hash仅仅是客户端的一个状态,也就是说,当向服务器发请求的时候,hash部分并不会发过去。

通过监听window对象的hashChange事件,可以实现简单的路由。例如:

window.onhashchange = function() {
  var hash = window.location.hash
  var path = hash.substring(1)

  switch (path) {
    case '/':
      showHome()
      break
    case '/users':
      showUsersList()
      break
    default:
      show404NotFound()
  }
}

(2) 基于History API
通过HTML5 History API可以在不刷新页面的情况下,直接改变当前URL。详细用法可以参考:

window.onpopstate = function() {
  var path = window.location.pathname

  switch (path) {
    case '/':
      showHome()
      break
    case '/users':
      showUsersList()
      break
    default:
      show404NotFound()
  }
}

但是这种方法只能捕获前进或后退事件,无法捕获pushState和replaceState,一种最简单的解决方法是替换pushState方法,例如:

var pushState = history.pushState
history.pushState = function() {
  pushState.apply(history, arguments)

  // emit a event or just run a callback
  emitEventOrRunCallback()
}

不过,最好的方法还是使用实现好的history库。
(3) 两种实现的比较
总的来说,基于Hash的路由,兼容性更好;基于History API的路由,更加直观和正式。

但是,有一点很大的区别是,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造。下面来详细分析。

假设服务器只有如下文件(script.js被index.html所引用):

/-
 |- index.html
 |- script.js

基于Hash的路径有:

http://example.com/
http://example.com/#/foobar

基于History API的路径有:

http://example.com/
http://example.com/foobar

当直接访问 http://example.com/ 的时候,两者的行为是一致的,都是返回了index.html文件。

当从http://example.com/ 跳转到http://example.com/#/foobar 或者http://example.com/foobar 的时候,也都是正常的,因为此时已经加载了页面以及脚本文件,所以路由跳转正常。

当直接访问http://example.com/#/foobar 的时候,实际上向服务器发起的请求是http://example.com/ ,因此会首先加载页面及脚本文件,接下来脚本执行路由跳转,一切正常。

当直接访问http://example.com/foobar 的时候,实际上向服务器发起的请求也是http://example.com/foobar, 然而服务器端只能匹配/而无法匹配/foobar,因此会出现404错误。

因此如果使用了基于History API的路由,需要改造服务器端,使得访问/foobar的时候也能返回index.html文件,这样当浏览器加载了页面及脚本之后,就能进行路由跳转了。

动态路由

上面提到的例子都是静态路由,也就是说,路径都是固定的。但是有时候我们需要在路径中传入参数,例如获取某个用户的信息,我们不可能为每个用户创建一条路由,而是在通过捕获路径中的参数(例如用户id)来实现。

例如在Express中:

app.get('/user/:id', (req, res, next) => {
  // ... ...
})

Flask中:

@app.route('/user/<user_id>')
def get_user_info(user_id):
    pass

严格路由

在很多情况下,会遇到/foobar和/foobar/的情况,它们看起来非常似,然而实际上有所区别,具体的行为也是视服务器设置而定。

Flask的文档中,提到,末尾有斜线的路径,类比于文件系统的一个目录;末尾没有斜线的路径,类比于一个文件。因此访问/foobar的时候,可能会重定向到/foobar/,而反过来则不会。

如果使用的是Express,默认这两者是一样的,也可以通过app.set来设置strict routing,来区别对待这两种情况。

原文链接:http://syaning.com/2017/01/10/web-route/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容