onBackPress 必须根据 options.from 判断来源才有效,仅对 'backbutton' 拦截并返回 true,'navigateBack' 必须放行;需配置 "navigationStyle": "custom" 和 iOS "popGesture": "close" 才能统一处理各端返回行为。

onBackPress 必须按 options.from 判断来源才有效
直接在页面里写 onBackPress() { return true } 会拦死所有返回行为:右上角按钮点不动、uni.navigateBack() 调用失败、页面栈卡住甚至白屏。关键不是“要不要拦截”,而是“谁触发的”。options.from 只有两个常用值:'backbutton'(物理键 / 导航栏按钮)和 'navigateBack'(JS 主动调用)。只对前者做处理,后者必须放行。
- 安卓物理键、iOS 侧滑(需配置
"popGesture": "close")、部分安卓导航栏返回按钮 →options.from === 'backbutton' -
uni.navigateBack()、右上角原生返回按钮("navigationStyle": "default"下不触发此钩子)、tabBar 页面跳转 → 不走onBackPress或from !== 'backbutton' - 漏掉
return true,弹窗还没显示,页面已退走;漏掉判断from,用户点右上角“返回”也失效
导航栏必须设为 "navigationStyle": "custom"
默认导航栏("navigationStyle": "default")下,左上角返回按钮是原生渲染的,onBackPress 完全不会回调——这不是你代码的问题,是平台限制。想让导航栏按钮和物理键走同一处理路径,必须在 pages.json 对应页面配置 "navigationStyle": "custom"。iOS 还要额外加 "popGesture": "close" 才能让侧滑进入 onBackPress 流程。
- Android 物理键不受此影响,但用户习惯点左上角,不设 custom 就等于漏掉主要入口
- 设了 custom 后,得自己实现返回图标和点击逻辑,否则左上角空白
- iOS 侧滑手势即使设了
popGesture: close,也可能因 WebView 内嵌页或滚动冲突失效,禁用比监听更可靠
双击退出必须统一放在 App.vue 管理
业务页面里的 onBackPress 只对当前页生效。首页双击退出、二级页弹确认这种策略,不能分散写在各页面。要在 App.vue 的 onLaunch 或 onShow 中初始化时间戳(如 this.lastBackTime = 0),然后在全局 onBackPress 里检查:
onBackPress(options) {
if (options.from !== 'backbutton') return false
const now = Date.now()
if (now - this.lastBackTime < 2000) {
uni.exitMiniProgram?.() || plus.runtime.quit()
} else {
uni.showToast({ title: '再按一次退出', icon: 'none' })
}
this.lastBackTime = now
return true
}
- 别在多个页面重复绑定
plus.key.addEventListener('backbutton'),它和onBackPress冲突,且仅 App-PLUS 有效 -
plus.key方式必须用// #ifdef APP-PLUS包裹,并等document.addEventListener('plusready')后注册 - 首页判断不能靠
getCurrentPages().length === 1,因为 tabbar 页面不在栈中,得结合路由路径或全局状态
H5 和小程序根本不能用 onBackPress
onBackPress 在 H5 平台不触发——浏览器地址栏后退、Alt+←、手势都绕过它,这是浏览器安全限制,不是 bug。微信/支付宝小程序也不支持该钩子。强行用 // #ifdef H5 包裹编译能过,但运行无效。
- H5 下降级方案:不弹确认,直接
history.back();若需防丢失,只能用beforeunload(仅限表单未提交等有限场景) - 小程序替代方案:用
onUnload存标志 +onShow检查跳转,但无法阻断返回动作,只能事后补救 - WebView 场景更复杂:安卓物理键默认关整个
<web-view></web-view>,需在onBackPress里先调webview.canBack()判断是否还能内部后退
真正难的不是写弹窗,而是让物理键、导航栏按钮、侧滑手势在不同端收敛到同一响应逻辑,同时不破坏 JS 主动跳转的语义。很多问题不是代码没写对,是没意识到 navigationStyle 配置、popGesture 开关、平台能力边界这些隐性约束。


















