1. 前言
最近使用 uniapp 开发的 H5 在测试中遇到的问题:有A、B两个页面,反复从A页面进入B页面后,在B页面会存在返回函数失效问题。经过测试发现,如果反复进入B页面的参数相同,在第三次进入后,返回就会失效,不管是 uniapp 自带的头部导航,还是 uni.navigateBack API 都会失效,不能从 B页面 返回 A页面。目前测试其他浏览器未出现这种情况。
2. 问题分析
由于看不到报错,所以只是单纯的怀疑 UC浏览器 使用了类似微信小程序的页面栈的机制,一个就是页面栈存在个数的限制,当页面栈到了一个限定的数量,就会报错,导致不能进行返回。比如微信小程序的页面栈我记得之前限制是10个,如果你两个页面相互跳转,不进行删除的话,第五次跳转就不会进行跳转,可以参考【微信小程序页面5层限制的一种解决方案】。如果是这种问题,那么解决问题的办法就只能将返回方法重写成关闭当前页面再跳转回目标页面。第二种就是相同参数的页面 UC浏览器 可能直接缓存了相同参数的页面,然后在判断了反复进入相同页面参数相同的次数。如果有不对的地方,请指正,谢谢!因为本人没有单独对 UC 浏览器进行了解,肯定对他的机制理解有误。
3. 测试
- 给地址带不同参数,反复点击,发现切换着点击,就不会出现返回失效问题。
- A页面 -> B页面?params=1
- A页面 -> B页面?params=2
- A页面 -> B页面?params=3
通过上边的示例如果来回切换点击,就不存在返回失效的情况,但是点击同一个参数的B页面,还是存在返回失效的问题。通过测试,只要每次跳转参数是不同的参数,那么返回就不会失效。
4. 跳转代码封装
- 根据传入的 opts 参数,对 url、appId、delta、type 进行解构赋值,如果 opts 不存在,则使用空对象 {}。这样可以获取到传递给 $route 函数的路由参数。
- 根据 url 判断是否需要跳转到第三方页面。如果 url 以 http 开头,则认为是第三方页面。如果当前是 H5 环境,使用 window.location.href 跳转到第三方页面;如果当前不是 H5 环境,则将 url 用 uni.setStorageSync 存储起来,并使用 uni.navigateTo 跳转到 pages/webview/index,由这个页面负责展示第三方网页的内容。
- 根据 appId 和 url 判断是否需要跳转到小程序内部的页面。如果 appId 和 url 都存在,使用 uni.navigateToMiniProgram 跳转到指定的小程序页面。
- 根据 delta 判断是否需要返回上一级或多级页面。如果 pages.length 大于 delta,使用 uni.navigateBack 返回上一级或多级页面;如果 pages.length 小于或等于 delta,则使用 uni.switchTab 跳转到首页。
- 根据 type 判断是否需要执行其他类型的跳转。如果 types.includes(type) 为 true,则根据 url 是否包含 ? 来构造新的 url,并将时间戳作为参数添加到 url 中。然后使用 uni[type].call(uni, opts) 来执行相应类型的跳转。
typescript
import { ev } from '../index';
const types = [
'navigateTo',
'redirectTo',
'reLaunch',
'switchTab',
'navigateBack'
]
export const $route = (opts) => {
let {
url = '',
appId,
delta,
type = 'navigateTo'
} = opts || {};
// 路由未成功跳转,当前路由不再执行跳转
ev.once(url, (done) => {
// 如果跳转第三方页面
if(url.startsWith('http')){
// #ifdef H5
window.location.href = url;
// #endif
// #ifndef MP-WEIXIN
uni.setStorageSync("webViewUrl", url)
uni.navigateTo({ url: `/pages/webview/index` })
// #endif
return false;
} else if(appId && url){
// 兼容微信小程序跳转
uni.navigateToMiniProgram({
appId,
path: url,
envVersion: 'release' // develop(开发版),trial(体验版),release(正式版)
})
} else if(delta){
let pages = getCurrentPages();
if(pages.length > delta){
// 如果返回页面数有效,直接执行返回
uni.navigateBack(opts)
} else {
uni.switchTab({url: `/pages/index/index`})
}
} else if(types.includes(type)){
if(url.includes('?')){
opts.url = `${url}&jump_timestamp=${Date.now()}`
} else {
opts.url = `${url}?jump_timestamp=${Date.now()}`
}
// 否则执行其他跳转
uni[type].call(uni, opts)
}
done();
})
}
5. 总结
- 注意:路由未成功跳转,当前路由不再执行跳转,此处是防止用户快速重复点击。
- 此处跳转对第三方路由,微信小程序跳转,返回上一页,最后跳转类型统一处理。
- 解决本文问题的方法就是添加 jump_timestamp 参数,形成每次跳转都是最新的,就能解决 UC 浏览器的返回失效问题。
- done 最后跳转完成,当前此跳转的监听释放,允许下次相同地址的跳转。