現(xiàn)在vueRouter的模式基本都是history模式趴拧,它的實(shí)現(xiàn)是基于原生事件popstate來(lái)實(shí)現(xiàn)的。
先看看MDN文檔對(duì)這個(gè)api的解釋?zhuān)?/p>
當(dāng)活動(dòng)歷史記錄條目更改時(shí)山叮,將觸發(fā)popstate事件著榴。如果被激活的歷史記錄條目是通過(guò)對(duì)
history.pushState()
的調(diào)用創(chuàng)建的,或者受到對(duì)history.replaceState()
的調(diào)用的影響屁倔,popstate事件的state屬性包含歷史條目的狀態(tài)對(duì)象的副本脑又。
需要注意的是調(diào)用history.pushState()
或history.replaceState()
不會(huì)觸發(fā)popstate事件。只有在做出瀏覽器動(dòng)作時(shí)锐借,才會(huì)觸發(fā)該事件问麸,如用戶點(diǎn)擊瀏覽器的回退按鈕(或者在Javascript代碼中調(diào)用history.back()
或者history.forward()
方法)
也就是說(shuō)我們點(diǎn)擊瀏覽器的前進(jìn)、后退按鈕或者在js中執(zhí)行history.back()
钞翔、history.forward()
就會(huì)觸發(fā)popstate事件严卖。
那這個(gè)api和vueRouter的history模式有什么關(guān)系呢?
我們知道vue項(xiàng)目其實(shí)是個(gè)單頁(yè)應(yīng)用布轿,大致結(jié)構(gòu)如下:
<html>
<body>
<div id="app">
<router-view />
</div>
</body>
</html>
所以我們?cè)陧?xiàng)目中各種跳轉(zhuǎn)切換頁(yè)面哮笆,都不會(huì)去請(qǐng)求html資源,都是在一個(gè)html中完成dom切換汰扭,也就是更換route-view標(biāo)簽里面的內(nèi)容稠肘,但是我們卻可以通過(guò)瀏覽器的前進(jìn)后退按鈕進(jìn)行頁(yè)面切換,實(shí)現(xiàn)的關(guān)鍵就是去監(jiān)聽(tīng)popstate事件萝毛,根據(jù)相關(guān)信息去更換route-view標(biāo)簽里面的內(nèi)容项阴。
到這里需要再介紹一個(gè)api,history.pushstate
同樣看一下MDN文檔的介紹:
在 HTML 文檔中珊泳,
history.pushState()
方法向當(dāng)前瀏覽器會(huì)話的歷史堆棧中添加一個(gè)狀態(tài)(state)鲁冯。
語(yǔ)法:history.pushState(state, title[, url])
這個(gè)api會(huì)往瀏覽器的歷史棧添加一個(gè)狀態(tài),舉個(gè)不太切當(dāng)?shù)睦樱?br>
比如現(xiàn)在在A頁(yè)面色查,然后從A頁(yè)面跳轉(zhuǎn)到B頁(yè)面薯演,然后在B頁(yè)面執(zhí)行history.pushState()
,此時(shí)點(diǎn)擊瀏覽器的后退按鈕不會(huì)返回A頁(yè)面秧了,而還是在B頁(yè)面跨扮。
兩個(gè)api的MDN文檔,推薦看一遍哦:
popstate
history.pushState
所以vueRouter的history模式簡(jiǎn)單來(lái)說(shuō)就是監(jiān)聽(tīng)popstate事件验毡,去執(zhí)行切換顯示dom的事件衡创;然后當(dāng)路由切換時(shí),執(zhí)行了history.pushState()
晶通,然后手動(dòng)觸發(fā)一遍切換顯示dom的事件璃氢。
現(xiàn)在回到標(biāo)題,在vue中如何攔截瀏覽器的前進(jìn)后退按鈕呢狮辽?最近剛好遇到一個(gè)需求一也,需要此功能來(lái)實(shí)現(xiàn):
有一個(gè)列表組件list:
<template>
<div class="list">
<Item />
<Item />
<Item />
...
</div>
<template/>
列表頁(yè)有很多數(shù)據(jù)巢寡,每條數(shù)據(jù)只展示基礎(chǔ)信息,然后點(diǎn)擊每條item椰苟,要展示每條item的詳情抑月,跳轉(zhuǎn)到詳情頁(yè)。
這種需求非常常見(jiàn)舆蝴,也有很多種辦法實(shí)現(xiàn)谦絮,比如動(dòng)態(tài)路由,但是如果是動(dòng)態(tài)路由洁仗,那么返回列表頁(yè)的時(shí)候层皱,列表頁(yè)就會(huì)刷新,在沒(méi)有keep-alive的情況京痢。交互不太友好奶甘,我一般都是通過(guò)組件切換去實(shí)現(xiàn):
<template>
<div class="list" v-show="!showDetail">
<Item />
<Item />
<Item />
...
</div>
<Deatil v-if="showDetail" />
<template/>
列表通過(guò)v-show顯示隱藏,詳情組件通過(guò)v-if來(lái)觸發(fā)初始化祭椰。但這樣寫(xiě)又有一個(gè)問(wèn)題臭家,當(dāng)顯示詳情頁(yè)的時(shí)候,通過(guò)瀏覽器的后退按鈕是回不到列表頁(yè)的方淤,因?yàn)樗麄冊(cè)谕瑐€(gè)組件同個(gè)路由中钉赁,只能通用自己寫(xiě)的返回按鈕來(lái)實(shí)現(xiàn),為了考慮用戶習(xí)慣的行為携茂,就要實(shí)現(xiàn)在詳情組件中點(diǎn)擊后退按鈕時(shí)能返回到列表頁(yè)你踩。
結(jié)合上面所說(shuō),在進(jìn)入詳情組件的時(shí)候讳苦,把這個(gè)行為當(dāng)成進(jìn)入一個(gè)新頁(yè)面带膜,所以就要往瀏覽器會(huì)話的歷史堆棧中添加一個(gè)狀態(tài):
// 顯示詳情組件的事件
// id是每一條數(shù)據(jù)的id,或者是一個(gè)標(biāo)識(shí)
const goDetail = (id) => {
showDetail = true
history.pushState({ ...history.state, id }, '')
}
history.state儲(chǔ)存了當(dāng)前瀏覽器會(huì)話的信息鸳谜,包括前進(jìn)是什么狀態(tài)膝藕,后退是什么狀態(tài),上面例子中我往里面添加了一個(gè)id咐扭;history.pushState有三個(gè)參數(shù)芭挽,第一個(gè)參數(shù)就是state,第二個(gè)參數(shù)可以忽略蝗肪,第三個(gè)參數(shù)url可選袜爪,vueRouter的切換就是通過(guò)第三個(gè)參數(shù)來(lái)指定,如果不傳第三個(gè)參數(shù)薛闪,當(dāng)前url就不會(huì)改變辛馆。
當(dāng)執(zhí)行了上面的goDetail方法后,就會(huì)顯示詳情組件豁延。假如列表頁(yè)的路由的 /list昙篙,從 /home路由跳轉(zhuǎn)過(guò)來(lái)的倔韭,那當(dāng)顯示詳情頁(yè)的時(shí)候,點(diǎn)擊瀏覽器的后退按鈕瓢对,因?yàn)橐呀?jīng)執(zhí)行了history.pushState,所以不會(huì)返回到/home路由胰苏,還是在/list路由硕蛹。
那么怎么實(shí)現(xiàn)當(dāng)在詳情組件的時(shí)候,點(diǎn)擊后退按鈕隱藏詳情組件硕并,顯示列表呢法焰?
這時(shí)候就需要監(jiān)聽(tīng)popstate事件了,我在history.pushState的時(shí)候倔毙,往state中添加了一個(gè)標(biāo)識(shí)埃仪,key為id,通過(guò)判斷是否有這個(gè)標(biāo)識(shí)來(lái)切換組件:
onMounted(() => {
window.addEventListener('popstate', toggleDetailShow)
})
const toggleDetailShow = () => {
if (history.state.id) {
showDetail = true
} else {
showDetail = false
}
}
當(dāng)顯示詳情組件的時(shí)候陕赃,點(diǎn)擊后退按鈕卵蛉,觸發(fā)了popstate事件,history.state回到未執(zhí)行history.pushState的狀態(tài)么库,也就是history.state沒(méi)有id這個(gè)標(biāo)識(shí)傻丝,所以會(huì)隱藏掉詳情組件;此時(shí)诉儒,再點(diǎn)擊瀏覽器的前進(jìn)按鈕葡缰,history.state就會(huì)返回到執(zhí)行了history.pushState的狀態(tài),有id這個(gè)標(biāo)識(shí)忱反,就會(huì)顯示詳情組件泛释。
這樣就顯示了通過(guò)瀏覽器的前進(jìn)后退來(lái)控制同個(gè)路由下組件的切換。
本文基于在一個(gè)組件中切換列表與某條數(shù)據(jù)的詳情需求來(lái)說(shuō)温算,對(duì)于涉及到的兩個(gè)history api還有很多理解不足的地方怜校,如果你有更好的見(jiàn)解,請(qǐng)指教米者。
我是鴨子韭畸,祝你幸福。