Vue Router 路由實現原理
一、概念
通過改變 URL,在不重新請求頁面的情況下,更新頁面視圖。
二、實現方式
更新視圖但不重新請求頁面,是前端路由原理的核心之一,目前在瀏覽器環境中這一功能的實現主要有2
種方式:
1.Hash --- 利用 URL 中的hash("#");
2.利用 History interface 在HTML5中新增的方法。
Vue 中,它是通過 mode 這一參數控制路由的實現模式:
const router=new VueRouter({ mode:'history', routes:[...] })
創建 VueRouter 的實例對象時,mode 以構造參數的形式傳入,如下代碼:

mode 參數:
1.默認 hash
2. history。如果瀏覽器不支持 history 新特性,則采用 hash
3. 如果不在瀏覽器環境下,就采用 abstract(Node環境下)
mode 區別:
1. mode:"hash" 多了 “#”
http://localhost:8080/#/login
2.mode:"history"
http://localhost:8080/recommend
HashHistory:
hash("#") 的作用是加載 URL 中指示網頁中的位置。
# 本身以及它后面的字符稱職位 hash,可通過 window.location.hash 獲取
特點:
1. hash 雖然出現在 url 中,但不會被包括在 http 請求中,它是用來指導瀏覽器動作的,對服務器端完全無用,因此,改變 hash 不會重新加載頁面。
2. 可以為 hash 的改變添加監聽事件:
window.addEventListener("hashchange",funcRef,false)
3. 每一次改變 hash(window.localtion.hash),都會在瀏覽器訪問歷史中增加一個記錄。
利用 hash 的以上特點,就可以來實現前端路由"更新視圖但不重新請求頁面"的功能了。
HashHistory 擁有兩個方法,一個是 push, 一個是 replace
1
|
兩個方法:HashHistory.push() 和 HashHistory.replace()
|
HashHistory.push() 將新路由添加到瀏覽器訪問歷史的棧頂

從設置路由改變到視圖更新的流程:
1
|
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
|
解析:
1 $router.push() //調用方法 2 HashHistory.push() //根據hash模式調用,設置hash並添加到瀏覽器歷史記錄(添加到棧頂)(window.location.hash= XXX) 3 History.transitionTo() //監測更新,更新則調用History.updateRoute() 4 History.updateRoute() //更新路由 5 {app._route= route} //替換當前app路由 6 vm.render() //更新視圖
transitionTo() 方法是父類中定義的是用來處理路由變化中的基礎邏輯的,push() 方法最主要的是對 window 的 hash 進行了直接賦值:
window.location.hash=route.fullPath
hash 的改變會自動添加到瀏覽器的訪問歷史記錄中。
那么視圖的更新是怎么實現的呢,我們來看看父類 History 中的 transitionTo() 方法:
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) { const route = this.router.match(location, this.current) this.confirmTransition(route, () => { this.updateRoute(route) ... }) } updateRoute (route: Route) { this.cb && this.cb(route) } listen (cb: Function) { this.cb = cb }
可以看到,當路由變化時,調用了Hitory
中的this.cb
方法,而this.cb
方法是通過History.listen(cb)
進行設置的,回到VueRouter
類定義中,找到了在init()
中對其進行了設置:
init (app: any /* Vue component instance */) { this.apps.push(app) history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) }
HashHistory.replace()
replace()方法與push()方法不同之處在於,它並不是將新路由添加到瀏覽器訪問歷史的棧頂,而是替換掉當前的路由

HTML5History
History interface 是瀏覽器歷史記錄棧提供的接口,通過back()、forward()、go()等方法,我們可以讀取瀏覽器歷史記錄棧的信息,進行各種跳轉操作。
從 HTML5開始,History interface 提供了2個新的方法:pushState()、replaceState() 使得我們可以對瀏覽器歷史記錄棧進行修改:
window.history.pushState(stateObject,title,url) window.history,replaceState(stateObject,title,url)
stateObject:當瀏覽器跳轉到新的狀態時,將觸發 Popstate 事件,該事件將攜帶這個 stateObject 參數的副本
title:所添加記錄的標題
url:所添加記錄的 url
這2
個方法有個共同的特點:當調用他們修改瀏覽器歷史棧后,雖然當前url
改變了,但瀏覽器不會立即發送請求該url
,這就為單頁應用前端路由,更新視圖但不重新請求頁面提供了基礎
1.push
與hash模式類似,只是將window.hash改為history.pushState
2.replace
與hash模式類似,只是將window.replace改為history.replaceState
3.監聽地址變化
在HTML5History的構造函數中監聽popState(window.onpopstate)

兩種模式比較
-
pushState設置的新URL可以是與當前URL同源的任意URL;而hash只可修改#后面的部分,故只可設置與當前同文檔的URL
-
pushState通過stateObject可以添加任意類型的數據到記錄中;而hash只可添加短字符串
-
pushState可額外設置title屬性供后續使用
-
history模式則會將URL修改得就和正常請求后端的URL一樣,如后端沒有配置對應/user/id的路由處理,則會返回404錯誤