什麼是路由?

1. 什麼是路由

在Web開發過程當中,常常會遇到『路由』的概念。那麼,到底什麼是路由?簡單來講,路由就是URL到函數的映射css

 

2. router和route的區別

route就是一條路由,它將一個URL路徑和一個函數進行映射,例如:html

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

這就是兩條路由,當訪問 /users 的時候,會執行 getAllUsers() 函數;當訪問 /users/count 的時候,會執行 getUsersCount() 函數。html5

而 router 能夠理解爲一個容器,或者說一種機制,它管理了一組 route。簡單來講,route 只是進行了URL和函數的映射,而在當接收到一個URL以後,去路由映射表中查找相應的函數,這個過程是由 router 來處理的。一句話歸納就是 "The router routes you to a route"。git

3. 服務器端路由

對於服務器來講,當接收到客戶端發來的HTTP請求,會根據請求的URL,來找到相應的映射函數,而後執行該函數,並將函數的返回值發送給客戶端。對於最簡單的靜態資源服務器,能夠認爲,全部URL的映射函數就是一個文件讀取操做。對於動態資源,映射函數多是一個數據庫讀取操做,也多是進行一些數據的處理,等等。github

以 Express 爲例,數據庫

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

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

這裏定義了兩條路由:express

  • 當訪問 / 的時候,會返回 index 頁面
  • 當訪問 /users 的時候,會從數據庫中取出全部用戶數據並返回
不只僅是URL

在 router 匹配 route 的過程當中,不只會根據URL來匹配,還會根據請求的方法來看是否匹配。例如上面的例子,若是經過 POST 方法來訪問 /users,就會找不到正確的路由。

4. 客戶端路由

對於客戶端(一般爲瀏覽器)來講,路由的映射函數一般是進行一些DOM的顯示和隱藏操做。這樣,當訪問不一樣的路徑的時候,會顯示不一樣的頁面組件。客戶端路由最多見的有如下兩種實現方案:flask

  • 基於Hash
  • 基於History API

 

(1) 基於Hashapi

 

咱們知道,URL中 # 及其後面的部分爲 hash。例如:瀏覽器

const url = require('url')
var a = url.parse('http://example.com/#/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 對象的 popstate 事件,來實現簡單的路由:

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

當直接訪問 / 的時候,二者的行爲是一致的,都是返回了 index.html 文件。

 

當從 / 跳轉到 /#/foobar 或者 /foobar 的時候,也都是正常的,由於此時已經加載了頁面以及腳本文件,因此路由跳轉正常。

 

當直接訪問 /#/foobar 的時候,實際上向服務器發起的請求是 /,所以會首先加載頁面及腳本文件,接下來腳本執行路由跳轉,一切正常。

 

當直接訪問 /foobar 的時候,實際上向服務器發起的請求也是 /foobar,然而服務器端只能匹配 / 而沒法匹配 /foobar,所以會出現404錯誤。

 

所以若是使用了基於History API的路由,須要改造服務器端,使得訪問 /foobar 的時候也能返回 index.html 文件,這樣當瀏覽器加載了頁面及腳本以後,就能進行路由跳轉了。

 

5. 動態路由

上面提到的例子都是靜態路由,也就是說,路徑都是固定的。可是有時候咱們須要在路徑中傳入參數,例如獲取某個用戶的信息,咱們不可能爲每一個用戶建立一條路由,而是在經過捕獲路徑中的參數(例如用戶id)來實現。

 

例如在 Express 中:

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

在 Flask 中:

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

6. 嚴格路由

在不少狀況下,會遇到 /foobar 和 /foobar/ 的狀況,它們看起來很是相似,然而實際上有所區別,具體的行爲也是視服務器設置而定。

 

在 Flask的文檔 中,提到,末尾有斜線的路徑,類比於文件系統的一個目錄;末尾沒有斜線的路徑,類比於一個文件。所以訪問 /foobar 的時候,可能會重定向到 /foobar/,而反過來則不會。

 

若是使用的是 Express,默認這二者是同樣的,也能夠經過 app.set 來設置 strict routing,來區別對待這兩種狀況。

相關文章
相關標籤/搜索