場景
一個帶可切換tab的移動端頁面,基本頁面交互如下:
1.用戶通過點擊選卡(tab)可切換不同內容。
2.該頁面還有霸屏的彈窗,當用戶做物理返回操作的時候,首先關閉彈窗,再返回到上一級頁面:
頁面并沒有記錄下當前選卡,所以當頁面刷新后,選卡及內容始終呈現的是默認設置的第一項。這本身并沒有太大的問題,然而……
新需求
用戶要求在刷新頁面后仍能停留在當前選卡!這個需求可以有多種方式實現,比如在每次切換選卡時都記錄下該選卡值,不論是通過 vuex 或者 storage,都不是最好的實現。通過把當前選卡項顯式地記錄在 URL 上(比如 vue-router 中的 params 或 query)讓頁面觀察路由的變化來決定切換到哪個選卡,這樣一來除非 URL 變化,否則無論如何都不會丟失當前選卡項的值:
http://www.something.com/#/page_a/tab1
這才是最佳實踐:
但這樣做帶來了新問題——因為選卡切換與 $router
互相影響,在切換選卡的時候也會同步修改 router,通常用 push
方法會生成新的路由歷史,于是當用戶想要通過物理返回直接回到上級頁面前,會先回放之前留下的選卡切換操作,直到回退完所有的操作歷史才能到達上級頁面:
顯然,用戶不想改變固有的操作習慣,仍然希望在當前頁面操作的物理返回能夠直接退到上級頁面!
于是,我們要解決這個問題,避免產生路由記錄記錄。
方案
vue-router 提供了 replace
方法,相對于 push
,它同樣會指向并跳轉到新路由,但是完全覆蓋當前路由,而不是追加到路由的歷史記錄中。
于是把 push
替換成 replace
就好了,就那么簡單!
首先添加路由的 param(也可以利用 query),它必須被顯式地申明在路徑上:
{
path: '/page_a/:tab', // 在路由中添加 param
name: 'page_a',
component: './views/page_a.vue'
}
頁面模板大致是這樣的:
<template>
<tabs>
<!-- 切換選卡時標識 tabSwitching -->
<tab
v-for="(tab,i) in tabs"
:key="i"
@click="tabIndex=i"
:class="{current:tabIndex===i}">
{{tab.label}}
</tab>
</tabs>
<component :is="tab"></component>
</template>
腳本部分,觀察路由 $route.params.tab
的變化,這就實現了需求的基本功能:
data(){
return{
tabIndex:0,
tabs:[
{label:'一',name:'tab1'},
{label:'二',name:'tab2'},
{label:'三',name:'tab3'}
]
}
},
computed: {
tab() {
return this.$route.params.tab || this.tabs[0].name
}
},
watch:{
// 觀察路由的param
tab: {
handler: function (tab) {
// 根據 $route.params.tab 設定當前選卡
this.tabIndex = this.tabs.findIndex(item => item.name === tab)
},
immediate: true
},
tabIndex(index) {
let tab = this.tabs[index].name;
// 切換后改變
this.$router.replace({ // 重點
params: {
tab: tab
}
})
}
},
methods: {
/** 觀察并控制霸屏彈窗的開啟狀態
* @returns Boolean 用以判斷是否阻斷路由進程
*/
watchShow() {
let stop=false
if (this.filterPersonShow) {
this.filterPersonShow = false;
stop = true;
} else if (this.filterShow) {
this.filterShow = false;
stop = true;
}
return stop
}
}
我們需要對路由行為做攔截,在物理返回操作發生時,當發現有未關閉的霸屏彈窗就主動關閉并停止路由進程:
beforeRouteLeave(to, from, next) {
if (this.watchShow()) {
next(false);
} else {
next();
}
}
以上代碼是從案例中截取的主要部分,簡化代碼只為了說明實現方式,并不保證能夠獨立運行。
至此,整個需求才算完整實現。