v2.6.0 新增全局返回守卫与 iOS 侧滑拦截,补齐跨端路由拦截能力

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-app onBackPress 同步返回限制
  • 返回值为移除函数,可动态注册/移除一次性守卫

2. 守卫链执行顺序

erlang 复制代码
返回操作触发
  → onBeforeBack(任一返回 false 即拦截,NAVIGATION_ABORTED)
  → beforeEach(支持中止/重定向)
  → beforeResolve
  → 全部放行后执行 uni.navigateBack / 浏览器后退
  → afterEach

返回守卫放行后复用 beforeEachbeforeResolve 完整链路,中止 / 重定向行为与完整导航一致。

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 支持 支持
相关推荐
lichenyang45336 分钟前
从「房间」到「实时通知」:用 NestJS + Socket.IO 实现团队邀请的完整工程实践
前端·后端
ly76891 小时前
Vue 3 从入门到工程实践:基础语法、组件化、路由、状态管理与项目部署
前端·javascript·vue.js
掘金酱1 小时前
TRAE Work 实战帮征文 | 获奖名单公示
前端·人工智能·后端
陆枫Larry1 小时前
Chromium和Chrome到底是什么关系?
前端
a1117762 小时前
模仿刀剑神域风格 作品集网页
前端·开源
a1117762 小时前
Elemental Sandbox(元素沙盒)-Three.js 游戏技能特效
前端·开源
用户69371750013842 小时前
前阵子刷屏的 Ox-Alpha 真身揭晓!GLM-5.3-Flash 上线,这价格真的杀疯了
前端·后端·github
四千岁2 小时前
抓包LangChain-了解LangChain的原理
前端·javascript·后端
南雨北斗2 小时前
TS方式构建Vue3 toast组件(一)
前端