前端兩種路由實現和使用場景

Minorjone發表於2020-01-30

在學習vue-router時,瞭解到前端路由的兩種模式,本文就前端路由,及其兩種模式的原理和使用場景,做一個整理總結。

前端路由概述

什麼是路由

路由這個概念最先是後端出現的,簡單來說路由就是用來跟後端伺服器進行互動的一種方式,通過不同的路徑,來請求不同的資源,請求不同的頁面是路由的其中一種功能。

前端路由的誕生

前端路由的出現要從 ajax 開始,有了 Ajax 後,使用者互動就不用每次都重新整理頁面,體驗帶來了極大的提升。隨著技術的發展,簡單的非同步已經不能滿足需求,所以非同步的更高階體驗出現了——SPA(單頁應用)。 SPA 的出現大大提高了 WEB 應用的互動體驗。在與使用者的互動過程中,不再需要重新重新整理頁面,獲取資料也是通過 Ajax 非同步獲取,頁面顯示變的更加流暢。 但由於 SPA 中使用者的互動是通過 JS 改變 HTML 內容來實現的,頁面本身的 url 並沒有變化,這導致了兩個問題:

  • SPA 無法記住使用者的操作記錄,無論是重新整理、前進還是後退,都無法展示使用者真實的期望內容。
  • SPA 中雖然由於業務的不同會有多種頁面展示形式,但只有一個 url,對 SEO 不友好,不方便搜尋引擎進行收錄。

前端路由就是為了解決上述問題而出現的。

什麼是前端路由

簡單的說,就是在保證只有一個 HTML 頁面,且與使用者互動時不重新整理和跳轉頁面的同時,為 SPA 中的每個檢視展示形式匹配一個特殊的 url。在重新整理、前進、後退和SEO時均通過這個特殊的 url 來實現。 為實現這一目標,我們需要做到以下二點:

  • 改變 url 且不讓瀏覽器像伺服器傳送請求。
  • 可以監聽到 url 的變化

接下來要介紹的 hash 模式和 history 模式,就是實現了上面的功能。

Hash模式

原理

  • 早期的前端路由的實現就是基於location.hash來實現的,location.hash的值就是URL中#後面的內容 其實現原理就是監聽#後面的內容來發起Ajax請求來進行區域性更新,而不需要重新整理整個頁面。
  • 使用hashchange事件來監聽 URL 的變化,以下這幾種情況改變 URL 都會觸發 hashchange 事件:瀏覽器前進後退改變 URL、a標籤改變 URL、window.location改變URL。

使用

//html
<ul id="menu">
  <li>
    <a href="#index">首頁</a>
  </li>
  <li>
    <a href="#news">資訊</a>
  </li>
  <li>
    <a href="#user">個人中心</a>
  </li>
</ul>
<div id="app"></div>

//js
function hashChange(e){
    let app = document.getElementById('app')
    switch (location.hash) {
      case '#index':
        app.innerHTML = '<h1>這是首頁內容</h1>'
        break
      case '#news':
        app.innerHTML = '<h1>這是新聞內容</h1>'
        break
      case '#user':
        app.innerHTML = '<h1>這是個人中心內容</h1>'
        break
      default:
        app.innerHTML = '<h1>404</h1>'
    }
}
window.onhashchange = hashChange
hashChange()
複製程式碼

優點

  • 相容低版本瀏覽器,Angular1.x和Vue預設使用的就是hash路由
  • 只有#符號之前的內容才會包含在請求中被髮送到後端,也就是說就算後端沒有對路由全覆蓋,但是不會返回404錯誤
  • hash值的改變,都會在瀏覽器的訪問歷史中增加一個記錄,所以可以通過瀏覽器的回退、前進按鈕控制hash的切換 會覆蓋錨點定位元素的功能

缺點

  • 不太美觀,#後面傳輸的資料複雜的話會出現問題

History模式

原理

  • history 提供了 pushState 和 replaceState 兩個方法來記錄路由狀態,這兩個方法改變 URL 不會引起頁面重新整理
  • history 提供類似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通過瀏覽器前進後退改變 URL 時會觸發 popstate 事件,通過pushState/replaceState或a標籤改變 URL 不會觸發 popstate 事件。好在我們可以攔截 pushState/replaceState的呼叫和a標籤的點選事件來檢測 URL 變化,所以監聽 URL 變化可以實現,只是沒有 hashchange 那麼方便。
  • pushState(state, title, url) 和 replaceState(state, title, url)都可以接受三個相同的引數。

使用

//html
<ul id="menu">
  <li>
    <a href="/index">首頁</a>
  </li>
  <li>
    <a href="/news">資訊</a>
  </li>
  <li>
    <a href="/user">個人中心</a>
  </li>
</ul>
<div id="app"></div>

//js
document.querySelector('#menu').addEventListener('click',function (e) {
  if(e.target.nodeName ==='A'){
    e.preventDefault()
    let path = e.target.getAttribute('href')  //獲取超連結的href,改為pushState跳轉,不重新整理頁面
    window.history.pushState({},'',path)  //修改瀏覽器中顯示的url地址
    render(path)  //根據path,更改頁面內容
  }
})

function render(path) {
  let app = document.getElementById('app')
  switch (path) {
    case '/index':
      app.innerHTML = '<h1>這是首頁內容</h1>'
      break
    case '/news':
      app.innerHTML = '<h1>這是新聞內容</h1>'
      break
    case '/user':
      app.innerHTML = '<h1>這是個人中心內容</h1>'
      break
    default:
      app.innerHTML = '<h1>404</h1>'
  }
}
window.onpopstate = function (e) {
  render(location.pathname)
}
render('/index')
複製程式碼

優點

  • 使用簡單,比較美觀
  • pushState()設定新的URL可以是任意與當前URL同源的URL,而hash只能改變#後面的內容,因此只能設定與當前URL同文件的URL
  • pushState()設定的URL與當前URL一模一樣時也會被新增到歷史記錄棧中,而hash#後面的內容必須被修改才會被新增到新的記錄棧中
  • pushState()可以通過stateObject引數新增任意型別的資料到記錄中,而hash只能新增短字串
  • pushState()可額外設定title屬性供後續使用

缺點

  • 前端的URL必須和向傳送請求後端URL保持一致,否則會報404錯誤
  • 由於History API的緣故,低版本瀏覽器有相容行問題

兩種不同使用場景

  • 從上文可見,hash模式下url會帶有#,當你希望url更優雅時,可以使用history模式。
  • 當使用history模式時,需要注意在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
  • 當需要相容低版本的瀏覽器時,建議使用hash模式。
  • 當需要新增任意型別資料到記錄時,可以使用history模式。

參考連結:

相關文章