为了解决这些问题,我们引入了无限层级路由方案。
首先声明一下,最初方案并不是我提出的,是我司内部一位清华学霸提出的。但他们是基于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,这次就介绍这么多,有问题或者有更好的方案,可以留言沟通,大家相互学习。