為了解決這些問題,我們引入了無限層級(jí)路由方案。
首先聲明一下,最初方案并不是我提出的,是我司內(nèi)部一位清華學(xué)霸提出的。但他們是基于wepy框架做的處理,由于我們用的是mpvue,所以對(duì)這個(gè)方案上做了修改,同時(shí)不依賴于框架。
雖然是改造版,但原理是一樣的,下面我來介紹一下修改后的方案。
圖示:

之前跳轉(zhuǎn)操作和10層以上的返回操作都會(huì)更新邏輯棧,到了10層以內(nèi)的返回操作就不會(huì)更新邏輯棧了。到這里細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn):
這塊也是我們對(duì)原有方案的主要改造點(diǎn)。因?yàn)榈搅?0層以內(nèi),所有的返回和跳轉(zhuǎn)都由微信系統(tǒng)歷史棧接管了。
我們只要保證用戶在通過api進(jìn)行跳轉(zhuǎn)操作時(shí)更新就可以了。而且,自己維護(hù)的邏輯路由棧實(shí)際上只有中轉(zhuǎn)頁才會(huì)用到。
這樣也就不用在每個(gè)頁面都要注冊(cè)onUnload鉤子去實(shí)時(shí)更新返回時(shí)的路由信息了。把更新路由信息的邏輯都放到了api調(diào)用這一層。業(yè)務(wù)開發(fā)時(shí)完全不用關(guān)心。
lib/navigator/Navigator.js (自己封裝的跳轉(zhuǎn)方法, History.js代碼省略了)
...
import History from '@/lib/navigator/History'
const MAX_LEVEL = 10 // 小程序支持打開的頁面層數(shù)
export default class Navigator {
// 中轉(zhuǎn)頁面路徑
static curtainPage = '/pages/curtain/curtain/main'
// 最大頁數(shù)
static maxLevel = MAX_LEVEL
// 邏輯棧
static _history = new History({
routes: [{ url: '' }],
correctLevel: MAX_LEVEL - 2
})
...
/**
* 打開新頁面
* @param {Object} route 頁面配置,格式同wx.navigateTo
*/
@makeMutex({ namespace: globalStore, mutexId: 'navigate' }) // 避免跳轉(zhuǎn)相關(guān)函數(shù)并發(fā)執(zhí)行
static async navigateTo (route) {
console.log('[Navigator] navigateTo:', route)
// 更新邏輯棧
Navigator._history.open({ url: route.url })
let curPages = getCurrentPages()
// 小于倒數(shù)第二層時(shí),直接打開
if (curPages.length < MAX_LEVEL - 1) {
await Navigator._secretOpen(route) // 就是調(diào)用wx.navigateTo
// 倒數(shù)第二層打開最后一層
} else if (curPages.length === MAX_LEVEL - 1) {
const url = URL.setParam(Navigator.curtainPage, { url: route.url })
await Navigator._secretReplace({ url }) // wx.redirectTo 到中轉(zhuǎn)頁,再由中轉(zhuǎn)頁跳轉(zhuǎn)到第10層頁面
// 已經(jīng)達(dá)到最大層數(shù),直接最后一層重定向
} else {
await Navigator._secretReplace(route) // wx.redirectTo 第10層頁面直接重定向
}
}
/**
* 完整歷史記錄
* @return {Array}
*/
static get history () {
return Navigator._history.routes
}
/**
* 更新路由
* @param {Object} config 自定義配置,可配置項(xiàng)參見 _config 相關(guān)字段及注釋
*/
static updateRoutes (routes = []) {
this._history._routes = routes
}
...
}
復(fù)制代碼
|
中轉(zhuǎn)頁代碼 /pages/curtain/curtain/index.vue
<template>
<div class="main"></div>
</template>
<script>
import Navigator from '@/lib/navigate/Navigator'
// query參數(shù)
let opts = null
// 是否為返回操作
let isBack = false
export default {
onLoad (options) {
// 緩存參數(shù)
opts = options
// 執(zhí)行onLoad生命周期,認(rèn)為是跳轉(zhuǎn)或者重定向操作
isBack = false
},
onShow () {
// 跳轉(zhuǎn)、重定向操作時(shí),邏輯棧的狀態(tài)會(huì)在跳轉(zhuǎn)函數(shù)里更新
if (!isBack) {
const url = decodeURIComponent(opts.url)
// 再返回時(shí)認(rèn)為是返回操作
isBack = true
// 跳轉(zhuǎn)操作
if (opts.type === 'navigateTo') {
Navigator._secretOpen({ url }) // 相當(dāng)直接執(zhí)行wx.navigateTo,不會(huì)更新邏輯棧
// 重定向
} else if (opts.type === 'redirectTo') {
Navigator._secretReplace({ url }) // 相當(dāng)直接執(zhí)行wx.redirectTo,不會(huì)更新邏輯棧
}
// 返回操作
} else {
// 獲取邏輯棧
let routes = Navigator.history
// 如果10層之外的返回,用navigateTo操作
// 如果是10層返回到9層,用redirectTo操作
const operation = (routes.length === Navigator.maxLevel) ? 'redirectTo' : 'navigateTo'
// 獲取要返回的頁面路由
let preRoute
if (operation === 'navigateTo') {
// 移除邏輯層中后兩個(gè)元素:
// 移除最后一個(gè)是因?yàn)橐?
// 移除倒數(shù)第二個(gè)是因?yàn)?,跳轉(zhuǎn)到倒數(shù)第二個(gè)頁面時(shí)會(huì)重新插入邏輯棧
preRoute = routes.splice(routes.length - 2, 2)[0]
} else {
// 重定向時(shí)只移除最后一個(gè)元素
preRoute = routes[routes.length - 2]
routes.splice(routes.length - 1, 1)
}
// 更新邏輯棧
Navigator.updateRoutes(routes)
// 執(zhí)行自己包裝的跳轉(zhuǎn)、重定向方法,該操作會(huì)更新邏輯棧
Navigator[operation](preRoute)
}
}
}
</script>
<style lang="scss">
.main {
background-color: $white-color;
}
</style>
復(fù)制代碼
|
原理就是這樣,但是有幾點(diǎn)需要注意:
切記不要直接調(diào)用wx的api,也不要使用組件,這樣是沒法更新js邏輯棧的,正確跳轉(zhuǎn)方式如:Navigator.navigateTo({ url: 'xxx' })。
這個(gè)方案最大的優(yōu)點(diǎn)在于不用監(jiān)聽頁面卸載時(shí)對(duì)邏輯棧的更新,無需在每個(gè)頁面里加入更新邏輯棧代碼。
OK,這次就介紹這么多,有問題或者有更好的方案,可以留言溝通,大家相互學(xué)習(xí)。