前端路由Hash與History模式

瞭解SPA

現代前端項目多爲單頁Web應用(SPA),在單頁Web應用中路由是其中的重要環節。
SPA 是 single page web application 的簡稱,譯爲單頁Web應用。
簡單的說 SPA 就是一個WEB項目只有一個 HTML 頁面,一旦頁面加載完成,SPA 不會由於用戶的操做而進行頁面的從新加載或跳轉。 取而代之的是利用 JS 動態的變換 HTML 的內容,從而來模擬多個視圖間跳轉。css

前度路由

簡單的說,就是在保證只有一個 HTML 頁面,且與用戶交互時不刷新和跳轉頁面的同時,爲 SPA 中的每一個視圖展現形式匹配一個特殊的 url。在刷新、前進、後退和SEO時均經過這個特殊的 url 來實現。
咱們須要實現下滿兩點:html

  • 改變 url 且不讓瀏覽器像服務器發送請求。
  • 能夠監聽到 url 的變化
  • 能夠在不刷新頁面的前提下動態改變瀏覽器地址欄中的URL地址

hash 模式和 history 模式,就是用來實現上面功能的前端

Hash模式

在url後面加上#,如http://127.0.0.1:5500/前端路由/hash.html#/page1這個url後面的#/page1就是hash值html5

  • hash 值的變化不會致使瀏覽器像服務器發送請求
  • location.hash能夠獲取hash值
  • hashchange是hash值發生改變的調用的函數

基於以上三點咱們能夠寫一個路由實例web

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <ul>
      <li><a href="#/">/</a></li>
      <li><a href="#/page1">page1</a></li>
      <li><a href="#/page2">page2</a></li>
    </ul>
    <div class="content-div"></div>
  </body>
  <script>
    class RouterClass {
      constructor() {
        this.routes = {}; // 記錄路徑標識符對應的cb
        this.currentUrl = ""; // 記錄hash只爲方便執行cb
        window.addEventListener("load", () => this.render());
        window.addEventListener("hashchange", () => this.render());
      }

      /* 初始化 */
      static init() {
        window.Router = new RouterClass();
      }

      /* 註冊路由和回調 */
      route(path, cb) {
        this.routes[path] = cb || function() {};
      }

      /* 記錄當前hash,執行cb */
      render() {
        this.currentUrl = window.location.hash.slice(1) || "/";
        this.routes[this.currentUrl]();
      }
    }


    RouterClass.init();
    const ContentDom = document.querySelector(".content-div");
    const changeContent = content => (ContentDom.innerHTML = content);

    Router.route("/", () => changeContent("默認頁面"));
    Router.route("/page1", () => changeContent("page1頁面"));
    Router.route("/page2", () => changeContent("page2頁面"));
  </script>
</html>

History模式

History 接口容許操做瀏覽器的曾經在標籤頁或者框架裏訪問的會話歷史記錄。能夠參考下兩篇文章對history的說明
https://css-tricks.com/using-the-html5-history-api/
https://developer.mozilla.org/zh-CN/docs/Web/API/History
下面介紹在這個模式下須要用到的apiajax

history基本api

  • history.go(n):路由跳轉幾步,n爲2往前跳轉2個頁面,-2日後跳轉兩個頁面
  • history.back():路由後退,至關於 history.go(-1),用戶可點擊瀏覽器左上角的後退按鈕模擬此方法
  • history.forward():路由前進,至關於 history.go(1),用戶可點擊瀏覽器左上角的前進按鈕模擬此方法

pushState()

history.pushState():添加一條路由歷史記錄,若是設置跨域網址則報錯segmentfault

history.pushState用於在瀏覽歷史中添加歷史記錄,可是並不觸發跳轉,此方法接受三個參數,依次爲:
state:一個與指定網址相關的狀態對象, popstate事件觸發時,該對象會傳入回調函數。若是不須要這個對象,此處能夠填 null
title:新頁面的標題,可是全部瀏覽器目前都忽略這個值,所以這裏能夠填 null
url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。

window的popstate事件

當活動歷史記錄條目更改時,將觸發popstate事件。若是被激活的歷史記錄條目是經過對history.pushState()的調用建立的,或者受到對history.replaceState()的調用的影響,popstate事件的state屬性包含歷史條目的狀態對象的副本。
須要注意的是調用history.pushState()或history.replaceState()不會觸發popstate事件。只有在作出瀏覽器動做時,纔會觸發該事件,如用戶點擊瀏覽器的回退按鈕(或者在Javascript代碼中調用history.back())後端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <ul>
      <li><a href="/">/</a></li>
      <li><a href="/page1">page1</a></li>
      <li><a href="/page2">page2</a></li>
    </ul>
    <div class="content-div"></div>
  </body>
  <script>
    class RouterClass {
      constructor(path) {
        this.routes = {}; // 記錄路徑標識符對應的cb
        history.replaceState({ path }, null, path); // 進入狀態
        this.routes[path] && this.routes[path]();
        window.addEventListener("popstate", e => {// 當用戶點擊瀏覽器的前進或者後退觸發
            console.log(e.state)
          const path = e.state && e.state.path;
          this.routes[path] && this.routes[path]();
        });
      }
      
      /* 初始化 */
      static init() {
        window.Router = new RouterClass(location.pathname);
      }

      /* 註冊路由和回調 */
      route(path, cb) {
        this.routes[path] = cb || function() {};
      }

      /* 跳轉路由,並觸發路由對應回調 */
      go(path) {
        
        history.pushState({ path }, null, path);
        console.log(history);
        this.routes[path] && this.routes[path]();
      }
    }

    RouterClass.init();
    const ul = document.querySelector("ul");
    const ContentDom = document.querySelector(".content-div");
    const changeContent = content => (ContentDom.innerHTML = content);

    Router.route("/", () => changeContent("默認頁面"));
    Router.route("/page1", () => changeContent("page1頁面"));
    Router.route("/page2", () => changeContent("page2頁面"));

    ul.addEventListener("click", e => {
      console.log(e.target.tagName);
      if (e.target.tagName === "A") {
        e.preventDefault();
        Router.go(e.target.getAttribute("href"));
      }
    });
  </script>
</html>

Hash 模式和 History 模式對比

Hash 模式是使用 URL 的 Hash 來模擬一個完整的 URL,所以當 URL 改變的時候頁面並不會重載。History 模式則會直接改變 URL,因此在路由跳轉的時候會丟失一些地址信息,在刷新或直接訪問路由地址的時候會匹配不到靜態資源。所以須要在服務器上配置一些信息,讓服務器增長一個覆蓋全部狀況的候選資源,好比跳轉 index.html 什麼的api

hash路由 優缺點

  • 優勢跨域

    • 實現簡單,兼容性好(兼容到ie8
    • 絕大多數前端框架均提供了給予hash的路由實現
    • 不須要服務器端進行任何設置和開發
    • 除了資源加載和ajax請求之外,不會發起其餘請求
  • 缺點

    • 對於部分須要重定向的操做,後端沒法獲取hash部份內容,致使後臺沒法取得url中的數據,典型的例子就是微信公衆號的oauth驗證
    • 服務器端沒法準確跟蹤前端路由信息
    • 對於須要錨點功能的需求會與目前路由機制衝突

History(browser)路由 優缺點

  • 優勢

    • 對於重定向過程當中不會丟失url中的參數。後端能夠拿到這部分數據
    • 絕大多數前段框架均提供了browser的路由實現
    • 後端能夠準確跟蹤路由信息
    • 能夠使用history.state來獲取當前url對應的狀態信息
  • 缺點

    • 兼容性不如hash路由(只兼容到IE10)
    • 須要後端支持,每次返回html文檔

參考文章

實例來源:http://www.javashuo.com/article/p-ybhejxib-r.html
官方文檔:https://developer.mozilla.org/en-US/docs/Web/API/History_API
優缺點比較:https://juejin.im/post/5b5ec5...

相關文章
相關標籤/搜索