v2.6.0 补全返回拦截能力。新增
router.onBeforeBack()全局返回守卫,覆盖 App 物理返回键 / 顶部导航栏返回 /uni.navigateBack、H5 浏览器后退按钮 / 后退手势;新增app.setSideSlipGesture控制 iOS 侧滑返回手势,使侧滑返回也能走守卫链。配套新增getPlatform()统一平台判断工具,替换散落的typeof window/typeof plus特殊判断。
前言
此前返回操作无法被路由守卫拦截:
- App 端 :物理返回键、顶部导航栏返回按钮、外部
uni.navigateBack均由系统直接处理,beforeEach等守卫不执行 - H5 端:浏览器后退按钮 / 后退手势由浏览器接管,同样绕过守卫
- iOS 侧滑返回 :原生手势直接返回,不触发
onBackPress onBeforeRouteLeave(v2.2.0)只能拦截经过路由器的导航(push/replace/back/relaunch),对上述原生返回入口无效
v2.6.0 通过全局 mixin 接入 onBackPress(App)+ popstate 事件(H5),实现返回守卫;配合 app.setSideSlipGesture 解决 iOS 侧滑绕过问题。方案借鉴 uni-simple-router 的全局 mixin 思路,无需 UTS 原生插件,保持跨端一致性。
一、功能总览
| 功能 | 说明 |
|---|---|
router.onBeforeBack() |
全局返回守卫,返回操作触发时执行;false 阻止、true/undefined 放行,支持异步 |
app.setSideSlipGesture |
App 平台 iOS 侧滑返回手势控制,'none' 禁用(走守卫链)/ 'close' 开启原生 |
getPlatform() |
统一平台判断工具(基于 uni.getSystemInfoSync(),带缓存),返回 PlatformInfo |
| 平台判断统一 | InterceptorPlugin 的 isWebPlatform() 改用 getPlatform().isH5 |
二、onBeforeBack - 全局返回守卫
1. API 设计
typescript
export type BackGuardReturn = boolean | void | Promise<boolean | void>
export type BackGuard = (to: RouteLocation, from: RouteLocation) => BackGuardReturn
// Router 接口新增
onBeforeBack(guard: BackGuard): () => void
to- 返回目标页面(上一页)from- 当前正要离开的页面- 返回
false阻止返回;true/undefined放行;支持异步(Promise),不受 uni-apponBackPress同步返回限制 - 返回值为移除函数,可动态注册/移除一次性守卫
2. 守卫链执行顺序
erlang
返回操作触发
→ onBeforeBack(任一返回 false 即拦截,NAVIGATION_ABORTED)
→ beforeEach(支持中止/重定向)
→ beforeResolve
→ 全部放行后执行 uni.navigateBack / 浏览器后退
→ afterEach
返回守卫放行后复用 beforeEach → beforeResolve 完整链路,中止 / 重定向行为与完整导航一致。
3. App 端接入(onBackPress)
通过全局 mixin 向每个页面注入 onBackPress,覆盖物理返回键 / 顶部导航栏返回 / navigateBack:
typescript
app.mixin({
onShow() {
router.syncRoute()
router.applySideSlipGesture()
},
onBackPress() {
return router.handleBackPress()
}
})
handleBackPress() 处理逻辑:
- 仅 App 平台拦截(
getPlatform().isApp),H5 / 小程序保持默认返回行为 - 递归保护 :路由器发起的返回(
router.back()/ 守卫放行后的手动返回)会再次触发onBackPress,通过backGuardRunning标记放行,避免守卫重复执行与死循环 - 根页面(页面栈 < 2)放行默认行为(如 Android 物理键退出应用)
- 其余情况返回
true阻止默认返回,异步执行守卫链(onBackPress必须同步返回,守卫链以 fire-and-forget 方式运行),放行后手动uni.navigateBack
4. H5 端接入(popstate)
通过 popstate 事件接入浏览器后退按钮 / 后退手势。popstate 触发时浏览器已完成后退,采用「撤销后退 → 守卫放行后重新后退」策略:
markdown
1. 记录当前页 URL(h5BackUrl),popstate 后 URL 已变化说明发生了后退
2. history.go(1) 恢复当前页(触发二次 popstate,命中「回到当前页」分支放行)
3. 同步捕获 to/from 执行返回守卫链
4. 守卫放行后重新后退(再次触发 popstate,由 backGuardRunning 放行)
5. 平台支持矩阵
| 返回入口 | 平台 | 可拦截 |
|---|---|---|
| 物理返回键 | App(Android) | ✅ |
| 顶部导航栏返回按钮 | App | ✅ |
uni.navigateBack |
App | ✅ |
| 浏览器后退按钮 / 后退手势 | H5 | ✅ |
| iOS 侧滑返回 | App(iOS) | 默认绕过;配置 app.setSideSlipGesture('none') 后 ✅ |
| 小程序原生返回 | 小程序 | ❌(无法拦截) |
三、iOS 侧滑返回手势控制(app.setSideSlipGesture)
1. API 设计
typescript
export type SideSlipGesture = 'none' | 'close'
export interface AppRouterOptions {
/** 在页面 onShow 时调用,返回 'none' 禁用手势 / 'close' 开启原生手势 */
setSideSlipGesture?: (to: RouteLocation) => SideSlipGesture
}
export interface RouterOptions {
// ...
/** App 平台专属配置(侧滑返回手势等) */
app?: AppRouterOptions
}
2. 实现细节
- 由全局 mixin 在页面
onShow时调用applySideSlipGesture() - 仅 iOS 平台生效(
getPlatform().isIOS),Android 使用物理返回键,由onBackPress拦截 - 通过
plus.webview.currentWebview()?.setStyle({ popGesture: value })动态修改手势 - 未配置
app.setSideSlipGesture时不干预手势,保持 uni-app 默认行为
3. 用法示例
typescript
const router = createRouter({
routes,
app: {
// 'none' 禁用侧滑返回(走守卫链,onBeforeBack 生效)
// 'close' 开启原生侧滑返回(保留手势,侧滑绕过守卫)
setSideSlipGesture(to) {
return to.meta.requireAuth ? 'none' : 'close'
}
}
})
四、getPlatform() - 平台判断工具
1. API 设计
typescript
export interface PlatformInfo {
isApp: boolean // 是否 App 平台(app / app-harmony)
isH5: boolean // 是否 H5 平台
isMp: boolean // 是否小程序平台(mp-*)
isIOS: boolean // 是否 iOS 系统
isAndroid: boolean // 是否 Android 系统
uniPlatform: string // uni-app 平台类型:'app' | 'web' | 'mp-weixin' 等
osName: string // 系统名称:'ios' | 'android' 等
}
export function getPlatform(): PlatformInfo
2. 兼容性回退
- 优先使用
uniPlatform/osName(HBuilderX 3.5.3+ 提供) - 旧版本不返回
uniPlatform时,回退到typeof plus/typeof window推断 App / H5 uni.getSystemInfoSync()异常时回退为空,按非 App / 非 H5 处理- 首次调用读取并缓存,之后直接返回
3. 新增类型声明
env.d.ts新增plus全局对象(含webview.currentWebview().setStyle())env.d.ts新增uni.getSystemInfoSync()返回类型(uniPlatform/osName/platform)
五、升级指南
v2.6.0 完全向后兼容,无破坏性变更:
- 新增 API(
onBeforeBack/app.setSideSlipGesture/getPlatform)均为可选能力,未使用不影响现有行为 - 未配置
app.setSideSlipGesture时,iOS 侧滑返回保持 uni-app 默认原生行为 - 返回守卫默认不注册,注册后仅对返回操作生效,不影响
push/replace/relaunch导航 - 新增类型导出:
BackGuard/BackGuardReturn/AppRouterOptions/SideSlipGesture/PlatformInfo
版本兼容性
| 功能 | v2.5.0 | v2.6.0 |
|---|---|---|
| 返回操作拦截(App 物理键/导航栏/navigateBack) | 不支持 | 支持(onBeforeBack) |
| H5 浏览器后退拦截 | 不支持 | 支持(popstate) |
| iOS 侧滑返回手势控制 | 不支持 | 支持(app.setSideSlipGesture) |
getPlatform() 平台判断 |
不支持 | 支持 |
| 完整导航守卫链(beforeEach/beforeResolve/afterEach) | 支持 | 支持 |
| 可控重定向 / 冷启动 guardRoute | 支持 | 支持 |