VueRouter路由模式全面解析
VueRouter路由模式
前端路由的實(shí)現(xiàn)方式主要有兩種:hash模式和history模式。
hash模式
在window.location對(duì)象中有一個(gè)hash字段,可以獲取地址欄中#字符及后邊的所有字符。
hash也稱(chēng)作錨點(diǎn),本身是用來(lái)做頁(yè)面定位的,可以使對(duì)應(yīng)id的元素顯示在可是區(qū)域內(nèi)。
比如說(shuō)下面這張動(dòng)圖,通過(guò)錨點(diǎn)直接跳轉(zhuǎn)到footer:

hash 雖然出現(xiàn)在 URL 中,但不會(huì)被包括在 HTTP 請(qǐng)求中,不會(huì)對(duì)請(qǐng)求有影響。
下圖可以看到雖然地址是http://localhost:3000/index.html#footer,但是network中的請(qǐng)求是http://localhost:3000/index.html。

由于hash值變化不會(huì)導(dǎo)致瀏覽器向服務(wù)器發(fā)出請(qǐng)求,而且hash改變會(huì)觸發(fā)hashchange事件,瀏覽器的進(jìn)后退也能對(duì)其進(jìn)行控制,所以在 html5 的 history 出現(xiàn)前,開(kāi)發(fā)者基本都是使用 hash 來(lái)實(shí)現(xiàn)前端路由的。
history模式
HTML5規(guī)范提供了history.pushState和history.replaceState來(lái)進(jìn)行路由控制。
通過(guò)這兩個(gè)方法可以改變url且不向服務(wù)器發(fā)送請(qǐng)求。
同時(shí)不會(huì)像hash有一個(gè)#,更加的美觀。
但是如果刷新頁(yè)面,那么請(qǐng)求的資源就是當(dāng)前地址欄中的地址了,對(duì)于SPA應(yīng)用來(lái)說(shuō)就需要進(jìn)行配置將所有的路由重定向到根頁(yè)面。
調(diào)用pushState跳轉(zhuǎn)到相同的路由時(shí)仍可以成功且路由棧數(shù)量會(huì)加1。
這個(gè)可以重寫(xiě)pushState方法來(lái)進(jìn)行過(guò)濾。
const stack = [];
const originPushState = window.history.pushState;
window.history.pushState = function() {
if(stack[stack.length - 1] === arguments[2]) {
return;
}
stack.push(arguments[2])
originPushState.call(history, ...arguments);
}history.replaceState 和 history.pushState 卻不會(huì)觸發(fā) popstate 事件,不過(guò)可以手動(dòng)創(chuàng)建一個(gè)PopStateEvent并觸發(fā)該事件。
function createPopStateEvent(state, title) {
return new PopStateEvent("popstate", {
state: state,
title: title
});
}
window.history.pushState = function() {
if(stack[stack.length - 1] === arguments[2]) {
return;
}
stack.push(arguments[2])
window.dispatchEvent(createPopStateEvent(...arguments));
originPushState.call(history, ...arguments);
}VueRouter中的路由模式
文章中介紹的vue-router源碼版本為v3.4.9。
hash模式
在hash模式中,VueRouter主要還是以history.pushState為首選,在不支持history的瀏覽器中才會(huì)通過(guò)改變location.hash的值來(lái)實(shí)現(xiàn)路由切換。
在源碼中的hash.js文件中的pushHash方法明確定義。
// src/history/hash.js
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
} else {
window.location.hash = path
}
}同時(shí)在beforeCreate監(jiān)聽(tīng)hashchange方法,防止用戶(hù)直接在地址欄上修改url。
通過(guò)點(diǎn)擊跳轉(zhuǎn)的方式VueRouter做了處理并不會(huì)觸發(fā)該方法。
const eventType = supportsPushState ? 'popstate' : 'hashchange' window.addEventListener( eventType, handleRoutingEvent )
hash模式為什么首先考慮使用history.pushState呢?
因?yàn)橥ㄟ^(guò)history.pushState可以設(shè)置state值,VueRouter在每個(gè)路由跳轉(zhuǎn)時(shí)帶有唯一的key,可以用于在瀏覽器后退前進(jìn)時(shí)恢復(fù)到之前的滾動(dòng)的位置。
在源碼中的pushState方法中可以看到,在跳轉(zhuǎn)前保存當(dāng)前路由的滾動(dòng)條位置,同時(shí)為跳轉(zhuǎn)路由添加一個(gè)新key標(biāo)識(shí)新路由。
function pushState (url?: string, replace?: boolean) {
saveScrollPosition()
const history = window.history
// safari存在問(wèn)題,路由棧中只能保存100,超過(guò)100個(gè)異常捕獲用location來(lái)替換路由對(duì)象
try {
if (replace) {
const stateCopy = extend({}, history.state)
stateCopy.key = getStateKey()
history.replaceState(stateCopy, '', url)
} else {
history.pushState({ key: setStateKey(genStateKey()) }, '', url)
}
} catch (e) {
window.location[replace ? 'replace' : 'assign'](url)
}
}history模式
在初始化history模式時(shí),監(jiān)聽(tīng)popstate,在路由變化時(shí)恢復(fù)滾動(dòng)條位置。
// src/history/html5.js
this.transitionTo(location, (route) => {
if (supportsScroll) {
handleScroll(router, route, current, true)
}
})
window.addEventListener('popstate', handleRoutingEvent)前面提到history.pushState和history.replaceState兩個(gè)方法不會(huì)觸發(fā)popstate方法,因此在push和replace方法中,在路由切換成功后手動(dòng)執(zhí)行hashScroll方法。
// src/history/html5.js
push(location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(
location,
(route) => {
pushState(cleanPath(this.base + route.fullPath))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
},
onAbort
)
}abstract模式
在VueRouter中除了hash和history兩種模式外,還新增了一個(gè)abstract模式,用來(lái)在不支持瀏覽器的環(huán)境中充當(dāng)一個(gè)備用方案(例如Weex)。
在abstract模式中新建了一個(gè)對(duì)象用來(lái)模擬瀏覽器中路由棧。
this.stack = []
同時(shí)提供了三個(gè)路由跳轉(zhuǎn)方法push、replace和go,這三種方法跟hash和history模式中的push、replace和go方法類(lèi)似,來(lái)看看是怎么實(shí)現(xiàn)的。
// src/history/abstract.js push()方法
route => {
// 在路由棧中添加一個(gè)新路由
this.stack = this.stack.slice(0, this.index + 1).concat(route)
this.index++
onComplete && onComplete(route)
}因?yàn)榉菫g覽器環(huán)境中沒(méi)有前進(jìn)后退按鈕,因此replace其實(shí)就是替換掉路由棧中的最后一個(gè)路由。
// src/history/abstract.js replace()方法
route => {
// 在路由棧中替換掉最后一個(gè)路由
this.stack = this.stack.slice(0, this.index).concat(route)
this.index++
onComplete && onComplete(route)
}在go方法中同樣對(duì)傳入的數(shù)字進(jìn)行判斷是否在路由棧的范圍內(nèi),否則不執(zhí)行。
// src/history/abstract.js go()方法
const targetIndex = this.index + n
if (targetIndex < 0 || targetIndex >= this.stack.length) {
return
}之后直接將路由索引指向需要跳轉(zhuǎn)的路由索引同時(shí)更新當(dāng)前路由。
// src/history/abstract.js go()方法
const route = this.stack[targetIndex]
this.confirmTransition(
route,
() => {
this.index = targetIndex
this.updateRoute(route)
// ...
)
// src/history/base.js
updateRoute(route: Route) {
this.current = route
this.cb && this.cb(route)
}無(wú)論是hash還是history模式都需要對(duì)瀏覽器當(dāng)前的URL產(chǎn)生影響,通過(guò)與abstract結(jié)合也可以實(shí)現(xiàn)在已存在的路由頁(yè)面中內(nèi)嵌其他的路由頁(yè)面,而保持在瀏覽器當(dāng)中依舊顯示當(dāng)前頁(yè)面的路由path。
比如定義了一個(gè)hash模式并存在兩個(gè)路由的實(shí)例掛載在app上,其中第二個(gè)路由中又定義一個(gè)abstract模式的路由。這時(shí)候切換/route1和/route2并不會(huì)影響URL。
const routes = [
{
path: '/',
component: { template: `<div>default view</div>` }
},
{
path: '/abstractRoute',
component: {
name: "abstract",
template: '<div><h1 @click="toRoute(1)">跳轉(zhuǎn)到router1</h1><h1 @click="toRoute(2)">跳轉(zhuǎn)到router2</h1><router-view></router-view></div>',
methods: {
toRoute(num) {
this.$router.push(`/route${num}`);
}
},
router: new VueRouter({
routes: [
{
path: '/route1',
component: {template:'<h1>abstract route 1</h1>'}
},
{
path: '/route2',
component: { template:'<h1>abstract route 2</h1>'}
},
],
mode: 'abstract'
})
}
},
]
const app = new Vue({
router: new VueRouter({
routes,
mode: 'hash'
}),
template: `<div><router-link to="/">to /</router-link>
<router-link to="/abstractRoute">to abstractRoute</router-link>
<router-view></router-view>
</div>`,
}).$mount('#app')總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例
這篇文章主要為大家介紹了Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Vue.js中vue-property-decorator的使用方法詳解
vue-property-decorator是一個(gè)用于在Vue.js中使用TypeScript裝飾器的庫(kù),它能夠簡(jiǎn)化 Vue 組件的定義,使代碼更加簡(jiǎn)潔和可維護(hù),它能夠簡(jiǎn)化Vue組件的定義,使代碼更加簡(jiǎn)潔和可維護(hù),本文將深入探討vue-property-decorator的使用方法,并展示如何在Vue.js項(xiàng)目中應(yīng)用它2024-08-08
Vue3?企業(yè)級(jí)組件庫(kù)框架搭建?pnpm?monorepo實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了Vue3?企業(yè)級(jí)組件庫(kù)框架搭建?pnpm?monorepo實(shí)戰(zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Vue2.0用 watch 觀察 prop 變化(不觸發(fā))
本篇文章主要介紹了Vue2.0用 watch 觀察 prop 變化(不觸發(fā)),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-09-09
Vue2中的過(guò)濾器filter使用及注意說(shuō)明
這篇文章主要介紹了Vue2中的過(guò)濾器filter使用及注意說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09

