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