有這樣一個需求:
頁面上有很多標簽,點擊后選中,并異步調取業(yè)務接口,要求:
- 不刷新頁面,同時將選中的標簽值反應在 URL 的 query 參數(shù)里;
- 當跳轉其他頁面完成業(yè)務操作后,再次返回標簽頁,選中 URL 上 query 參數(shù)對應的標簽值*
通常改變 URL 使用 Vue Router(v4.x) 的 router.push()、router.replace() 方法:
router.replace({
query: {
tag: selectedTag.value
}
})
但是他們都會立即跳轉頁面,可能打斷用戶的操作。JS 原生的 history.replaceState 用于操作 history 記錄,它不會立即刷新頁面,用它來替換 URL 的 query 再合適不過了:
export function updateQuery(query:{
[key:string]: string|undefined
}={}){
const url=new URL(window.location.href)
// 根據(jù)方法參數(shù)重置query
Object.keys(query).forEach(key=>{
url.searchParams.set(key,query[key]??'')
})
window.history.replaceState(null, '', url)
}
在標簽上綁定click事件并調用:
updateQuery({
tag: selectedTag.value,
})
當點擊標簽時,就會發(fā)現(xiàn)瀏覽器地址欄的 URL 如預期的發(fā)生著變化,并且頁面沒有重新加載。
但是,很快就發(fā)現(xiàn):當切換到下一個頁面,再返回(瀏覽器的返回上一頁操作),此時之前給定的參數(shù)并未出現(xiàn)再URL的query里,是的,query參數(shù)丟了!
瀏覽器調試區(qū)出現(xiàn)了警告,告訴我們應該閱讀下 Vue Router 的官方說明,顯然是哪里漏了關鍵內容。在文檔里,找到了以下描述:
“Vue Router 將信息保存在 history.state 上。”
“我們使用歷史狀態(tài)(history.state)來保存導航信息,如滾動位置,以前的地址等?!?/p>
原來,Vue Router 在 history 模式下依賴 state 信息,所以若想在 Vue Router 驅動的路由框架內使用 history.replaceState()、history.pushState() 等 JS 原生方法,是需要把 history.state 帶入的。讓我們先來看看此時 history.state 里都有些什么內容:
{
"back": "/xxxx/detail?id=546546465",
"current": "/trade-manage/after-sale?tag=6",
"forward": "/xxxx/detail?id=546546465",
"replaced": true,
"position": 51,
"scroll": {
"left": 0,
"top": 0
}
}
從語義上就可以知道,它包含了前進、后退操作指向的地址,甚至是滾動條位置等信息。通過適當?shù)恼{整這些參數(shù),Vue Router 會依照調整后的內容執(zhí)行相關操作。
如此,在執(zhí)行 history.replaceState() 前改變下 history.state.current (它表示當前地址信息)的值,再把當前的 history.state 傳入 history.replaceState() 的第一個參數(shù)來手動更新下當前路由的 history.state。這樣在跳轉到下一條路由后 Vue Router 會依據(jù)自身邏輯主動把 history.state.current 的值填充到下一條路由的 history.state.back 上,從而決定在下一條路由下執(zhí)行“回退”操作時指向的地址。經過梳理最終得到了以下方法:
export function updateQuery(query:{
[key:string]: string|undefined
}={}){
const url=new URL(window.location.href)
const state=window.history.state
// 根據(jù)方法參數(shù)重置query
Object.keys(query).forEach(key=>{
url.searchParams.set(key,query[key]??'')
})
// 變更current,在跳轉前明確當前頁面的url,包括query
state.current=url.href.replace(new RegExp(`^${url.origin}`),'')
// 將state置入
window.history.replaceState(state, '', url)
}
簡單理解就是:每次變更 URL 的 query 時,同步更新下 history.state.current。
由此,問題解決,需求得到了滿足。