【vuejs進階】 vuejs路由實現原理

通常源碼中,都會用到 window.historylocation.hash

history 實現

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

history.back()
與在瀏覽器點擊後退按鈕相同
history.forward()
與在瀏覽器中點擊按鈕向前相同
history.go(n)

接受一個整數做爲參數,移動到該整數指定的頁面,好比go(1)至關於forward(),go(-1)至關於back(),go(0)至關於刷新當前頁面
若是移動的位置超出了訪問歷史的邊界,以上三個方法並不報錯,而是靜默失敗前端

在HTML5,history對象提出了 pushState() 方法和 replaceState() 方法,這兩個方法能夠用來向歷史棧中添加數據,就好像 url 變化了同樣(過去只有 url 變化歷史棧纔會變化),這樣就能夠很好的模擬瀏覽歷史和前進後退了,如今的前端路由也是基於這個原理實現的。vue

history.pushState

pushState(stateObj, title, url) 方法向歷史棧中寫入數據,其第一個參數是要寫入的數據對象(不大於640kB),第二個參數是頁面的 title, 第三個參數是 url (相對路徑)。vue-router

stateObj :一個與指定網址相關的狀態對象,popstate事件觸發時,該對象會傳入回調函數。若是不須要這個對象,此* 處能夠填null。
title:新頁面的標題,可是全部瀏覽器目前都忽略這個值,所以這裏能夠填null。
url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。
關於pushState,有幾個值得注意的地方:後端

pushState方法不會觸發頁面刷新,只是致使history對象發生變化,地址欄會有反應,只有當觸發前進後退等事件(back()和forward()等)時瀏覽器纔會刷新
這裏的 url 是受到同源策略限制的,防止惡意腳本模仿其餘網站 url 用來欺騙用戶,因此當違背同源策略時將會報錯數組

history.replaceState

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

popstate事件

定義:每當同一個文檔的瀏覽歷史(即history對象)出現變化時,就會觸發popstate事件。
注意:僅僅調用pushState方法或replaceState方法 ,並不會觸發該事件,只有用戶點擊瀏覽器倒退按鈕和前進按鈕,或者使用JavaScript調用back、forward、go方法時纔會觸發。另外,該事件只針對同一個文檔,若是瀏覽歷史的切換,致使加載不一樣的文檔,該事件也不會觸發。
用法:使用的時候,能夠爲popstate事件指定回調函數。這個回調函數的參數是一個event事件對象,它的state屬性指向pushState和replaceState方法爲當前URL所提供的狀態對象(即這兩個方法的第一個參數)。服務器

HISTORY實現SPA前端路由代碼
<a class="spa">abc.html</a>
<a class="spa">123.html</a>
<a href="/rdhub" class="spa ">rdhub</a>
  // 註冊路由
  document.querySelectorAll('.spa').forEach(item => {
    item.addEventListener('click', e => {
      e.preventDefault();
      let link = item.textContent;
      if (!!(window.history && history.pushState)) {
        // 支持History API
        window.history.pushState({name: 'history'}, link, link);
      } else {
        // 不支持,可以使用一些Polyfill庫來實現
      }
    }, false)
  });

  // 監聽路由
  window.addEventListener('popstate', e => {
    console.log({
      location: location.href,
      state: e.state
    })
  }, false)
popstate監聽函數裏打印的e.state即是history.pushState()裏傳入的第一個參數,在這裏即爲{name: 'history'}

hash

hash基本介紹

url 中能夠帶有一個 hash http://localhost:9000/#/rdhub.html

window 對象中有一個事件是 onhashchange,如下幾種狀況都會觸發這個事件:網絡

  1. 直接更改瀏覽器地址,在最後面增長或改變#hash;
  2. 經過改變location.href或location.hash的值;
  3. 經過觸發點擊帶錨點的連接;
  4. 瀏覽器前進後退可能致使hash的變化,前提是兩個網頁地址中的hash值不一樣。

hash實現SPA前端路由代碼

<a href="/rdhub" class="spa">rdhub</a>
<a href="/abc" class="spa">abc</a>
<a href="/123" class="spa">123</a>
<a href="/hash" class="spa">hash</a>
  document.querySelectorAll('.spa').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)

hash模式與history模式,這兩種模式都是經過瀏覽器接口實現的,除此以外vue-router還爲非瀏覽器環境準備了一個abstract模式,其原理爲用一個數組stack模擬出瀏覽器歷史記錄棧的功能。固然,以上只是一些核心邏輯,爲保證系統的魯棒性源碼中還有大量的輔助邏輯,也很值得學習。app

兩種模式比較

pushState設置的新URL能夠是與當前URL同源的任意URL;而hash只可修改#後面的部分,故只可設置與當前同文檔的URL

pushState設置的新URL能夠與當前URL如出一轍,這樣也會把記錄添加到棧中;而hash設置的新值必須與原來不同纔會觸發記錄添加到棧中

pushState經過stateObject能夠添加任意類型的數據到記錄中;而hash只可添加短字符串

pushState可額外設置title屬性供後續使用

history模式的一個問題

咱們知道對於單頁應用來說,理想的使用場景是僅在進入應用時加載index.html,後續在的網絡操做經過Ajax完成,不會根據URL從新請求頁面,可是不免遇到特殊狀況,好比用戶直接在地址欄中輸入並回車,瀏覽器重啓從新加載應用等。

hash模式僅改變hash部分的內容,而hash部分是不會包含在HTTP請求中的:

http://rdhub.cn/#/user/id // 如從新請求只會發送http://rdhub.cn/
故在hash模式下遇到根據URL請求頁面的狀況不會有問題。

而history模式則會將URL修改得就和正常請求後端的URL同樣
http://rdhub.cn/user/id
在此狀況下從新向後端發送請求,如後端沒有配置對應/user/id的路由處理,則會返回404錯誤。

官方推薦的解決辦法是在服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。同時這麼作之後,服務器就再也不返回 404 錯誤頁面,由於對於全部路徑都會返回 index.html 文件。爲了不這種狀況,在 Vue 應用裏面覆蓋全部的路由狀況,而後在給出一個 404 頁面。或者,若是是用 Node.js 做後臺,可使用服務端的路由來匹配 URL,當沒有匹配到路由的時候返回 404,從而實現 fallback。

參考資料:

  1. 瀏覽器History API :https://developer.mozilla.org...
  2. 解決History模式訪問404的方案:https://router.vuejs.org/zh/g...

關於咱們

https://www.yuque.com/rdhub/a...

關注訂閱號

2018-10-20-18-44-29

相關文章
相關標籤/搜索