vue 單頁應用(spa)前端路由實現原理

nightZing發表於2018-03-31

寫在前面:通常 SPA 中前端路由有2種實現方式:

  • window.history
  • location.hash

下面就來介紹下這兩種方式具體怎麼實現的

一.history

1.history基本介紹

window.history 物件包含瀏覽器的歷史,window.history 物件在編寫時可不使用 window 這個字首。history是實現SPA前端路由是一種主流方法,它有幾個原始方法:

  • history.back() - 與在瀏覽器點選後退按鈕相同
  • history.forward() - 與在瀏覽器中點選按鈕向前相同
  • history.go(n) - 接受一個整數作為引數,移動到該整數指定的頁面,比如go(1)相當於forward(),go(-1)相當於back(),go(0)相當於重新整理當前頁面
  • 如果移動的位置超出了訪問歷史的邊界,以上三個方法並不報錯,而是靜默失敗

在HTML5,history物件提出了 pushState() 方法和 replaceState() 方法,這兩個方法可以用來向歷史棧中新增資料,就好像 url 變化了一樣(過去只有 url 變化歷史棧才會變化),這樣就可以很好的模擬瀏覽歷史和前進後退了,現在的前端路由也是基於這個原理實現的。

2.history.pushState

pushState(stateObj, title, url) 方法向歷史棧中寫入資料,其第一個引數是要寫入的資料物件(不大於640kB),第二個引數是頁面的 title, 第三個引數是 url (相對路徑)。

  • stateObj :一個與指定網址相關的狀態物件,popstate事件觸發時,該物件會傳入回撥函式。如果不需要這個物件,此處可以填null。
  • title:新頁面的標題,但是所有瀏覽器目前都忽略這個值,因此這裡可以填null。
  • url:新的網址,必須與當前頁面處在同一個域。瀏覽器的位址列將顯示這個網址。

關於pushState,有幾個值得注意的地方:

  • pushState方法不會觸發頁面重新整理,只是導致history物件發生變化,位址列會有反應,只有當觸發前進後退等事件(back()和forward()等)時瀏覽器才會重新整理
  • 這裡的 url 是受到同源策略限制的,防止惡意指令碼模仿其他網站 url 用來欺騙使用者,所以當違背同源策略時將會報錯

3.history.replaceState

replaceState(stateObj, title, url) 和pushState的區別就在於它不是寫入而是替換修改瀏覽歷史中當前紀錄,其餘和 pushState一模一樣

4.popstate事件

  • 定義:每當同一個文件的瀏覽歷史(即history物件)出現變化時,就會觸發popstate事件。
  • 注意:僅僅呼叫pushState方法或replaceState方法 ,並不會觸發該事件,只有使用者點選瀏覽器倒退按鈕和前進按鈕,或者使用JavaScript呼叫back、forward、go方法時才會觸發。另外,該事件只針對同一個文件,如果瀏覽歷史的切換,導致載入不同的文件,該事件也不會觸發。
  • 用法:使用的時候,可以為popstate事件指定回撥函式。這個回撥函式的引數是一個event事件物件,它的state屬性指向pushState和replaceState方法為當前URL所提供的狀態物件(即這兩個方法的第一個引數)。

5.history實現spa前端路由程式碼

<a class="api a">a.html</a>
<a class="api b">b.html</a>
複製程式碼
 // 註冊路由
    document.querySelectorAll('.api').forEach(item => {
      item.addEventListener('click', e => {
        e.preventDefault();
        let link = item.textContent;
        if (!!(window.history && history.pushState)) {
          // 支援History API
          window.history.pushState({name: 'api'}, link, link);
        } else {
          // 不支援,可使用一些Polyfill庫來實現
        }
      }, false)
    });

    // 監聽路由
    window.addEventListener('popstate', e => {
      console.log({
        location: location.href,
        state: e.state
      })
    }, false)

複製程式碼

popstate監聽函式裡列印的e.state便是history.pushState()裡傳入的第一個引數,在這裡即為{name: 'api'}

二.Hash

1.Hash基本介紹

url 中可以帶有一個 hash http://localhost:9000/#/a.html

window 物件中有一個事件是 onhashchange,以下幾種情況都會觸發這個事件:

  • 直接更改瀏覽器地址,在最後面增加或改變#hash;
  • 通過改變location.href或location.hash的值;
  • 通過觸發點選帶錨點的連結;
  • 瀏覽器前進後退可能導致hash的變化,前提是兩個網頁地址中的hash值不同。

2.Hash實現spa前端路由程式碼

    // 註冊路由
    document.querySelectorAll('.api').forEach(item => {
      item.addEventListener('click', e => {
        e.preventDefault();
        let link = item.textContent;
        location.hash = link;
      }, false)
    });

    // 監聽路由
    window.addEventListener('hashchange', e => {
      console.log({
        location: location.href,
        hash: location.hash
      })
    }, false)

複製程式碼

相關文章