为了解决这些问题,咱们引入了无限层级路由方案。css
首先声明一下,最初方案并非我提出的,是我司内部一位清华学霸提出的。但他们是基于wepy框架作的处理,因为咱们用的是mpvue,因此对这个方案上作了修改,同时不依赖于框架。vue
虽然是改造版,但原理是同样的,下面我来介绍一下修改后的方案。小程序
几个关键点:api
图示:缓存
用户操做 | 小程序历史栈 | js逻辑栈 | 后续操做 |
---|---|---|---|
1 2 3 4 5 6 7 8 | 1 2 3 4 5 6 7 8 | 1 2 3 4 5 6 7 8 | 跳转页面9 |
1 2 3 4 5 6 7 8 9 | 1 2 3 4 5 6 7 8 9 | 1 2 3 4 5 6 7 8 9 | 跳转页面A |
1 2 3 4 5 6 7 8 9 A | 1 2 3 4 5 6 7 8 中 A | 1 2 3 4 5 6 7 8 9 A | 跳转页面B |
1 2 3 4 5 6 7 8 9 A B | 1 2 3 4 5 6 7 8 中 B | 1 2 3 4 5 6 7 8 9 A B | 跳转页面C |
1 2 3 4 5 6 7 8 9 A B C | 1 2 3 4 5 6 7 8 中 C | 1 2 3 4 5 6 7 8 9 A B C | 返回 |
1 2 3 4 5 6 7 8 9 A B | 1 2 3 4 5 6 7 8 中 B | 1 2 3 4 5 6 7 8 9 A B | 返回 |
1 2 3 4 5 6 7 8 9 A | 1 2 3 4 5 6 7 8 中 A | 1 2 3 4 5 6 7 8 9 A | 返回 |
1 2 3 4 5 6 7 8 9 | 1 2 3 4 5 6 7 8 9 | 1 2 3 4 5 6 7 8 9 | 返回(逻辑栈不更新) |
1 2 3 4 5 6 7 8 | 1 2 3 4 5 6 7 8 | 1 2 3 4 5 6 7 8 9 | 返回(逻辑栈不更新) |
1 2 3 4 5 6 7 | 1 2 3 4 5 6 7 | 1 2 3 4 5 6 7 8 9 | 返回(逻辑栈不更新) |
1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 9 | 返回(逻辑栈不更新) |
1 2 3 4 5 | 1 2 3 4 5 | 1 2 3 4 5 6 7 8 9 | 跳转页面6 |
1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 跳转页面7 |
1 2 3 4 5 6 7 | 1 2 3 4 5 6 7 | 1 2 3 4 5 6 7 | ... |
到这里细心的读者可能已经发现:bash
以前跳转操做和10层以上的返回操做都会更新逻辑栈,到了10层之内的返回操做就不会更新逻辑栈了。微信
这块也是咱们对原有方案的主要改造点。由于到了10层之内,全部的返回和跳转都由微信系统历史栈接管了。并发
咱们只要保证用户在经过api进行跳转操做时更新就能够了。并且,本身维护的逻辑路由栈实际上只有中转页才会用到。框架
这样也就不用在每一个页面都要注册onUnload钩子去实时更新返回时的路由信息了。把更新路由信息的逻辑都放到了api调用这一层。业务开发时彻底不用关心。async
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,此次就介绍这么多,有问题或者有更好的方案,能够留言沟通,你们相互学习。