移動端作為網際網路重要入口,切圖仔前端工程師開發移動端頁面早已是司空見慣的事了,並且其中大部分都是內嵌於客戶端(app
,小程式)的 h5
頁面,即 webview
有次被同事問到一個問題,他有個需求,是一個內嵌在客戶端app
內的頁面,有個功能點是攔截客戶端的返回操作,實現頁面內彈層的顯示與隱藏,攔截點包括 app
頁面上自帶的返回按鈕以及安卓機的物理按鈕,類似於下面這種:
點選購物車,則購物車元素從頁面底部彈起顯示,點選遮罩層,則彈層關閉隱藏,這是很正常的操作,而除此之外,還需要實現的一個功能是,點選螢幕返回鍵或者安卓機的物理返回鍵,也能夠關閉彈層,並且保證頁面不發生跳轉
我聽了微微一笑,問他,哪個傻叉 客戶端那邊有提供攔截返回的 PM
提的需求?,h5
怎麼攔截 app
甚至是物理按鈕的操作?你當時沒有懟他嗎?sdk
介面嗎?同事回答 sdk
沒有提供這個能力,他覺得不好實現,但 PM
堅持要做這個功能
於是我抱著反正不是我的需求,我天馬行空章口就來瞎提建議也沒關係助人為樂的態度思考了一會,誰知還真讓我想到了一個方案(我不確定以前是不是在什麼地方看到過,總之就是想到了),回來驗證了一下,確實是可行的
關鍵點在於,利用返回操作會觸發路由改變的特性來模擬達到攔截的效果,並不是真的監聽或者攔截到了螢幕返回鍵或者物理返回鍵的點選
路由配置
假設需要進行模擬攔截返回操作的主頁面路由為 /physicsBack
,其下有個子路由 /physicsBack/footerModal
,當路由為 /physicsBack
時就只顯示頁面,當路由為 /physicsBack/footerModal
時,就在 /physicsBack
上彈起彈層
這裡的 /physicsBack/footerModal
說起來是子路由,但實際上我們只是想利用其作為路由的一個能力——即攔截返回操作,所以實際上並不真的需要為這個路由配置一個頁面,你當然也可以這麼做,最後也能實現效果,但未免麻煩了些
這裡我將 /physicsBack
和 /physicsBack/footerModal
全部指向同一個頁面,即主頁面,然後通過對路由的監聽,來控制彈層的顯隱
路由配置如下:
const router = new VueRouter({
routes: [{
path: '/physicsBack/(footerModal)?',
component: physicsBack
}]
})
複製程式碼
/physicsBack/(footerModal)?
同時匹配 /physicsBack
和 /physicsBack/footerModal
,所以無論路由是 /physicsBack
還是 /physicsBack/footerModal
,都將指向 physicsBack
這個頁面,達到即使路由在這兩個中來回切換,但頁面也毫無變化的目的
路由監聽
雖然在路由 /physicsBack
和 /physicsBack/footerModal
中切換不會引起頁面的切換,自始至終都停留在 physicsBack
元件上,但卻可以在 physicsBack
元件中對路由進行監聽,進而根據監聽到的路由變化來控制彈層的顯隱:
watch: {
$route (to, from) {
this.manageFooterModal(to.path, from.path)
}
}
// ...
manageFooterModal (toPath, fromPath) {
if (toPath === '/physicsBack/footerModal') {
this.visible = true
} else if (fromPath === '/physicsBack/footerModal') {
this.visible = false
}
}
複製程式碼
當 toPath
是 /physicsBack/footerModal
,表示將切換到這個路由,前面已經規定了,當路由切換到這個位置時,顯示彈層;
當 fromPath
是 /physicsBack/footerModal
,表示從有彈層的頁面回退或者跳走,則關閉彈層
這裡你不用 watch
也是可以的,用 vue-router
提供的路由守衛鉤子函式(beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
)也能夠達到同樣的效果,這些鉤子函式只是進一步簡化了流程,但本質都還是一樣的,目的都是實現對路由的監聽與控制,所以你哪怕不想依賴於框架提供的能力,通過設定原生監聽函式 window.addEventListener('hashchange', callback)
或 window.addEventListener('popstate', callback)
照樣可以實現功能
除了通過路由控制之外,頁面元素肯定也必須能夠對彈層顯隱進行控制,例如點選某個元素彈起彈層,這樣才貼合真實使用場景
同樣的,由於彈層的顯隱實際上是由路由的切換控制,所以頁面內部想要改變彈層的顯隱,也必須通過路由切換來完成:
changeVisible () {
if (this.visible) {
this.$router.go(-1)
} else {
this.$router.push('/physicsBack/footerModal')
}
}
複製程式碼
由於這實際上是利用了系統的返回能力,所以無論你在瀏覽器上還是客戶端 app
的 webview
內,無論是使用螢幕返回還是物理返回鍵,都可以達到同樣的效果
實現的效果如下:
做了個 Live Demo,感興趣的可以親自試下,程式碼也已經上傳到 github
小結
本文所說的通過路由來模擬攔截返回鍵的能力,不僅僅可以應用在彈層例子上,其他跟返回相關的操作理論上都可以發揮想象,例如返回重定向,禁止使用者退出頁面(這是什麼傻叉需求啊)等