onBackPress 必须首行 return true,后续逻辑异步执行;仅对物理返回键生效,需在 App.vue 中实现;双击判断用时间戳差值而非计数器;平台差异需条件编译;非首页禁用该逻辑。

onBackPress 必须第一行 return true
不立刻拦截,系统就直接退栈——弹窗都来不及显示,用户点完返回键,页面已经切走了。这是最常踩的坑,90% 的“双击没反应”或“点一下就退”都源于此。
错误写法:if (res.confirm) { plus.runtime.quit(); } 再 return true;正确写法:函数体第一行就是 return true,后续所有逻辑(弹窗、计时、退出)必须异步执行。
- 只对物理返回键生效:加
if (options.from !== 'backbutton') return false过滤,避免干扰uni.navigateBack()等 JS 调用 - 不能写在页面组件里,必须放在
App.vue的onBackPress中,否则切页后逻辑丢失 - 如果当前页是 tabbar 页面,
uni.navigateBack()可能静默失败,得 fallback 到plus.runtime.quit()
双击判断靠时间戳,不是计数器
“双击”本质是两次触发间隔 ≤ 2000ms,不是“按了两次”。用 this.lastBackTime 记录上一次时间戳,比 this.clickCount++ 更可靠,也规避了清零时机难把握的问题。
变量必须挂载在 App.vue 实例上(如 data 或 export default 外层 this),不能是局部变量,否则每次触发都是新值。
- 每次触发取
Date.now(),与this.lastBackTime比差值 - 差值 plus.runtime.quit()(Android)或提示(iOS/H5)
- 否则弹
uni.showToast({ title: '再按一次退出' }),并更新this.lastBackTime - 清零必须用
setTimeout(() => { this.lastBackTime = 0; }, 2000),箭头函数确保this绑定正确
平台差异必须用条件编译包裹
plus.runtime.quit() 只在 Android 生效;iOS 静默失败;H5 根本不触发 onBackPress。不加包裹,编译或运行时直接报错或行为异常。
- Android:
// #ifdef APP-PLUS包裹plus.runtime.quit() - iOS:改用
uni.showToast({ title: '双击 Home 键退出', icon: 'none' }),或调plus.runtime.launchApplication({ action: 'QUIT' })跳转设置 - H5:不走
onBackPress,需单独监听history.back(),且仅当getCurrentPages().length === 0时才可用(刷新后页面栈为空) - 鸿蒙:需用 UTS 插件调
context.terminateSelf(),不能复用plus.runtime.quit()
非首页必须禁用该逻辑
子页、详情页、表单页不该拦截返回键——否则用户无法正常后退。只应在页面栈最底层启用,也就是首页。
用 getCurrentPages().length === 1 判断是否在栈底。注意:登录页、协议页等不可退页面也建议临时禁用。
- tabbar 页面可能因路由机制导致
uni.navigateBack()失败,此时需 fallback 到退出逻辑 - iOS 侧滑返回根本不进
onBackPress,得在pages.json关掉"popGesture": "close"才能触发 - 部分安卓厂商限制
quit()后自启,移到后台比强行退出更稳妥
实际最难处理的是 this 绑定和清零时机:setTimeout 里漏写箭头函数,this.lastBackTime = 0 就赋值到全局对象上;H5 页面刷新后 getCurrentPages() 为空,uni.navigateBack() 静默失败,这些细节不验证就上线,双击逻辑大概率在真机上失效。










