首頁  >  文章  >  web前端  >  詳解vuejs的路由實現原理

詳解vuejs的路由實現原理

不言
不言轉載
2018-10-24 10:28:322635瀏覽

這篇文章帶給大家的內容是關於詳解vuejs的路由實現原理,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

一般原始碼中,都會用到window.history 和location.hash

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 變化歷史棧才會變化),這樣就可以很好的模擬瀏覽歷史和前進後退了,現在的前端路由也是基於這個原理實現的。

history.pushState

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

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模擬出瀏覽器歷史記錄棧的功能。當然,以上只是一些核心邏輯,為確保系統的穩健性原始碼中還有大量的輔助邏輯,也很值得學習。

兩種模式比較

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。

#

以上是詳解vuejs的路由實現原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除