Vue-router路由原理
目前實現路由的方式有兩中,vue通過參數mode來設置,默認是hash模式。
- 利用URL中的hash(‘#’)來實現
- 利用History interface在HTML5中新增的方法
history對應的是HTML5History對象,hash對應的是HashHistory對象,abstract對應的是AbstractHistory對象。在初始化對應的history之前,會對mode做一些校驗:若瀏覽器不支持HTML5History方式(通過supportsPushState遍歷判斷),則mode設為hash,若不是在瀏覽器環境下運行,則mode設為abstract
VueRouter類中的onReady(),push()等方法只是一個代理,實際是調用的具體history對象的對應方法,在init()方法中初始化時,也是根據history對象具體的類別執行不同操作
HashHistory
Hash(‘#’)符號的本來作用是加在URL指示網頁中的位置,#本身以及它后面的字符稱之為hash,通過window.location.hash屬性來獲取
Hash雖然出現在url中,但不會被包括在http請求中,它是用來指導瀏覽器動作的,對服務端完全無用,因此改變hash不會重新加載頁面,如果是手動在url添加hash,這時可以為hash的改變添加監聽事件
Window.addEventListener(‘hashchange’,funcRef,false)
每一次改變hash(window.location.hash)都會在瀏覽器訪問歷史中增加一個記錄
執行vue里的push()方法最主要的是對window的hash進行直接賦值
Window.location.hash = route.fullPath
$router.push() -> HashHistory.push() -> History.transitionTo() -> History.updateRoute() -> vm.render()
HashHistory.replace()
Replace()方法與push()方法不同之處在於,它並不是將新路由添加到瀏覽器訪問歷史棧頂,而是替換掉當前的路由,背后它實際上是調用window.location.replace方法將路由進行替換
監聽地址欄
VueRouter.push()和VueRouter.replace()是可以在vue組件的邏輯代碼中直接調用的,除此之外在瀏覽器中,用戶還可以手動在瀏覽器地址欄中輸入url,因此還需要監聽瀏覽器地址欄中路由的變化,並具有與通過代碼調用的相同響應行為,在History中通過setupListeners監聽hashchange實現:
Window.addEventListener(‘hashchange’,function () {}),當在瀏覽器地址欄直接輸入路由相當於代碼調用了replace()方法
HTML5History
History interface是瀏覽器歷史記錄棧提供的接口,back(),forward(),go()等方法,HTML5提供了pushState(),replateState()這兩個方法
Window.history.pushState(stateObject,title,url)
Window.history.replateState(stateObject,title,url)
stateObject:當瀏覽器跳轉到新的狀態時,將觸發popState事件,該事件將攜帶這個stateObject參數的副本
title:所添加記錄的標題
url:所添加記錄的url
這兩個方法有個共同特點:當調用他們修改瀏覽器歷史棧后,雖然當前url改變了,但瀏覽器不會立即發送請求該url,這就為單頁應用前端路由,更新視圖但不重新請求頁面提供了基礎
History與hash模式基本類似,只不過將對window.location.hash()直接進行賦值window.location.replate()改為調用了window.location.history.pushState()和window.location.replateState()方法,而HTML5History中添加對修改瀏覽器地址欄url的監聽popstate是直接在構造函數中執行的
Window.addEventListeners(‘popstate’,e => {// 執行相應的功能})
HTML5History需要瀏覽器的支持,vue是通過supportPushState()來檢查
除這兩中模式外,vue-router還未非瀏覽器環境准備了一個abstract模式,原理是用一個數組stack模擬除瀏覽器歷史記錄棧的功能
兩中模式比較
調用history.pushState()相比直接修改hash主要有以下優勢:
- 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://baidu.com/#user/id //如請求,只會發送http://baidu.com
所以hash模式下遇到根據url請求頁面不會有問題
而history模式則將url修改的就和政策請求后端的url一樣,history不帶#。http://baidu.com/user/id
如果這種向后端發送請求的話,后端沒有配置對應/user/id的路由處理,會返回404錯誤
官方推薦的解決方法是在服務端增加一個覆蓋所有情況的候選資源:如果url匹配不到任何靜態資源,則應該返回同一個index.html頁面,這個頁面就是你app依賴的頁面。同時這么做以后,服務器就不再返回404錯誤頁面,因為對於所有路徑都會返回index.html文件。為了避免這種情況,在vue應用里面覆蓋所有的路由情況,然后再給出一個404頁面。或者如果是用node.js做后台,可以使用服務端的路由來匹配url,當沒有匹配到路由的時候返回404,從而實現fallback