ionic + vue3 + capacitor遇到backButton问题

项目背景:ionic + vue3 + capacitor

需求描述:需要通过 capacitor 提供的 backButton 方法,来监听安卓原生的返回按键事件。

1、bug:使用capacitor 封装的 backButton 方法来监听安卓原生返回键,如果不是根页面的情况,点击返回键,路由出现了两次回退(正常情况是只回退一次)。

2、addListener('backButton', async () => {})使用方法参考 capacitor 官方文档:

addListener(eventName: 'backButton', listenerFunc: (data: AppUrlOpen) => void) => PluginListenerHandle

监听硬件返回按钮事件(仅限 Android)。监听此事件会禁用默认的返回按钮行为,因此您可能需要手动调用window.history.back()。如果要关闭应用,请调用App.exitApp()

3、找到问题所在

因为文档里说使用这个监听方法会禁用掉原生返回按键的默认行为,可能需要手动添加window.history.back()事件,所以我也认为默认行为被禁用掉,我就自定义返回事件。

const rootPages = [ '/login', '/tabs/index', ] /** * 初始化返回键监听 */ const initBackButtonListener = async () => { if (!Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android') return // 移除已有监听,避免重复绑定 if (_backButtonListener) { await _backButtonListener.remove() } _backButtonListener = await App.addListener('backButton', async () => { const currentPath = router.currentRoute.value?.path || '' const isRootPage = rootPages.includes(currentPath) try { // 判断是根页面,则退出应用 if (isRootPage) { await exitApp() } else { // 不是根页面,返回上一级 window.history.back() } } catch (err) { console.error('返回键逻辑执行失败:', err) } }) }

但是这里我使用这个方法,并没有禁用掉原生返回按钮的默认行为,导致我在点击返回按键的时候,原生按钮触发1次返回,我又自定义了1次返回,两个方法叠加了,于是就出现了开头说的,点击返回按键1次,路由回退2次的现象。这个不知道是组件的bug还是什么导致的,不是很清楚,找了网上各路道友的经验贴,有处理结果的帖子很少,大多都是提的问题。

后面自己不断地安装排查,发现就是上述原因导致,于是我将自定义的路由返回注释掉,判断当前是否为根页面,是根页面,则点击返回按键退出应用;不是根页面,这里不做 window.history.back() 处理,需注释掉,默认原生返回按钮事件,返回上一级页面,功能就正常了。

除此之外,还需注意路由的跳转方式的区别,使用 push 和 replace 也会对退出应用有影响,一级页面互相跳转建议使用 replace 去跳转,避免点击返回键退出应用时,发生路由回退现象。

4、完整代码

appStatus.ts

里面有一个监听应用状态(应用切换前/后台)事件的方法、一个监听返回按键事件的方法和移出全部监听事件的方法。将方法在 App.vue 里面去进行初始化,代码如下:

import { useUserStore } from '@/store/user' import { App } from '@capacitor/app' import { Capacitor, type PluginListenerHandle } from '@capacitor/core' import { defineStore } from 'pinia' import { useRouter } from 'vue-router' export const useAppStatusStore = defineStore('appStatus', () => { // 是否前台,默认可见 const isForeground = ref<boolean>(!document.hidden) const userStore = useUserStore() const router = useRouter() /** * 清除 Token 并跳转登录页(后台切换时调用) */ const clearTokenAndRedirect = () => { try { // 清除 Token userStore.logout() // 跳转登录页 if (router.currentRoute.value?.path !== '/login') { router.push('/login').catch((err) => { console.warn('跳转登录页失败:', err) }) } // showToast('登录状态已失效,请重新登录') } catch (err) { console.error('清除 Token 或跳转登录失败:', err) } } // 网页端监听 const handleVisibility = () => { isForeground.value = !document.hidden if (document.hidden) clearTokenAndRedirect() } // 应用状态监听器-原生App监听 let _appStateListener: PluginListenerHandle | undefined let _pauseListener: PluginListenerHandle | undefined /** * App原生监听 */ const initAppStateListener = async () => { if (Capacitor.isNativePlatform()) { // 监听 appStateChange 事件,获取app应用状态(后台/前台) _appStateListener = await App.addListener( 'appStateChange', (state: { isActive: boolean }) => { isForeground.value = state.isActive // console.log('应用状态:', state.isActive ? '前台' : '后台') // 切后台时清除 Token if (!state.isActive) { clearTokenAndRedirect() } }, ) // 监听 pause 事件,增强后台检测 _pauseListener = await App.addListener('pause', () => { // console.log('应用进入暂停(后台)') clearTokenAndRedirect() }) } else { document.addEventListener('visibilitychange', handleVisibility) } } /** * 主动退出应用(仅支持安卓),退出前清除 Token */ const exitApp = async () => { if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === 'android') { userStore.logout() // 退出前清除 Token await App.exitApp() // 调用主动退出方法 } } // 返回键监听器-原生应用监听 let _backButtonListener: PluginListenerHandle | undefined // 连续按返回键的定时器,避免误触 // let _exitTimer: NodeJS.Timeout | null = null // 定义根页面,如果还有其他的根页面,往里加就行 const rootPages = [ '/login', '/tabs/index', ] /** * 初始化返回键监听 */ const initBackButtonListener = async () => { if (!Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android') return // 移除已有监听,避免重复绑定 if (_backButtonListener) { await _backButtonListener.remove() } _backButtonListener = await App.addListener('backButton', async () => { const currentPath = router.currentRoute.value?.path || '' const isRootPage = rootPages.includes(currentPath) try { // 判断是根页面,则退出应用 if (isRootPage) { await exitApp() // // 根页面,则处理退出应用 // if (_exitTimer) { // // 第二次按返回键:清除定时器 + 退出应用 // clearTimeout(_exitTimer) // _exitTimer = null // // 退出前清除 Token // userStore.logout() // // 调用 exitApp() 退出应用,仅支持安卓 // await App.exitApp() // } // else { // // 第一次按返回键:提示“再按一次退出” // showToast('再按一次返回键退出应用') // _exitTimer = setTimeout(() => { // _exitTimer = null // }, 2000) // 2秒内未再次按则重置 // } } else { // 不是根页面,这里不做 window.history.back() 处理,需注释掉,默认原生返回按钮事件,返回上一级页面 // window.history.back() } } catch (err) { console.error('返回键逻辑执行失败:', err) } }) } // 销毁监听 const removeAllListeners = async () => { // 销毁应用切换后台及退出应用事件监听 if (_appStateListener) await _appStateListener.remove() if (_pauseListener) await _pauseListener.remove() // 销毁返回按键事件监听 if (_backButtonListener) { await _backButtonListener.remove() _backButtonListener = undefined } // if (_exitTimer) { // clearTimeout(_exitTimer) // _exitTimer = null // } // 移动端移除全部监听 await App.removeAllListeners() // 网页端移除监听 if (!Capacitor.isNativePlatform()) { document.removeEventListener('visibilitychange', () => handleVisibility) } } return { isForeground, initAppStateListener, exitApp, removeAllListeners, clearTokenAndRedirect, initBackButtonListener, } })

App.vue

当组件挂在后初始化应用状态监听

<script setup lang="ts"> import { useAppStatusStore } from '@/store/appStatus' import { useUserStore } from '@/store/user' import { debounce } from 'lodash' const _ = (window as any).ResizeObserver; (window as any).ResizeObserver = class ResizeObserver extends _ { constructor(callback: (...args: any[]) => void) { callback = debounce(callback, 100) super(callback) } } const appStatusStore = useAppStatusStore() const userStore = useUserStore() const router = useRouter() if (userStore.userInfo.token) { router.replace('/tabs') } else { router.replace('/login') } onMounted(async () => { // 初始化应用状态监听 await appStatusStore.initAppStateListener() // console.log('当前是否前台:', appStatusStore.isForeground) // 初始化返回键监听 await appStatusStore.initBackButtonListener() }) // 组件销毁时清理所有监听 onUnmounted(async () => { await appStatusStore.removeAllListeners() }) </script> <template> <IonApp> <IonRouterOutlet /> </IonApp> </template>

以上是使用 capacitor 时踩的坑,自己记录一下攒攒经验,也希望能对你有用哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1125725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据领域数据标注的创新技术与趋势

大数据时代的数据标注革命&#xff1a;创新技术、实践痛点与未来趋势 摘要/引言 你知道训练一个能精准识别肺癌的AI模型需要多少标注数据吗&#xff1f;答案是至少5万张带病灶定位的CT影像——而这只是医疗AI领域的“基础需求”。当我们迈入大数据时代&#xff0c;全球每天产生…

解锁云计算的极致潜能

云计算的价值释放&#xff0c;离不开网络基础设施的协同支撑。“云改数转智惠”战略背景下&#xff0c;云网融合已成为云计算发展的核心趋势&#xff0c;通过“一朵云、一张网、一个平台”的架构设计&#xff0c;实现资源调度效率的质的飞跃。天翼云提出的云网一体化调度技术&a…

TritonNext 大会倒计时2天!这份高质量参会指南,请查收!

倒计时 2 天&#xff01;TritonNext 2026 聚焦下一代算子编程语言与真实落地实践&#xff0c;面向一线开发者的技术大会即将开启。报名通道仍在开放&#xff0c;速戳 &#x1f449;&#x1f3fb;https://www.bagevent.com/event/9117276?bag_trackcsdngzh2026年1月9日&#xf…

1的个数 与 十进制到八进制

1的个数按照十进制转二进制的逻辑&#xff0c;在循环中对余数为1的情况。#include<bits/stdc.h> using namespace std; int main(){int N;cin >> N;int count 0;while(N >0){//循环将N分解为2的幂次和if(N%2 1){count;//统计余数为1的次数}N/ 2;}cout <<…

什么是流策略

文章目录为什么需要流策略如何实现流策略流策略的典型应用流策略是一种网络流量管理机制&#xff0c;能够将具有相同特征的报文划分为一类&#xff0c;并为这一类报文提供相同的服务。通过配置流策略&#xff0c;用户可以定义报文分类规则匹配需要单独处理的流量&#xff0c;然…

sv中的三种case

在SystemVerilog中&#xff0c;case、casex和 casez是三种不同的条件选择语句&#xff0c;它们在匹配规则上有重要区别&#xff1a;1. 基本区别语句通配符匹配规则可综合程度case无精确匹配完全可综合casez?和 z将 z和 ?视为"不关心"可综合&#xff0c;但需谨慎cas…

当前Agent主流框架简介

LangChain干什么&#xff1a;把“外部 API 向量库 LLM”用链条&#xff08;Chain&#xff09;和记忆&#xff08;Memory&#xff09;串成可复用模板。适用&#xff1a;需要多步推理、调用外部工具&#xff08;搜索、计算器、数据库&#xff09;的问答或自动化流程。优点&…

2026年权威GEO优化服务商推荐TOP8榜|企业精准选择全指南

随着生成式AI技术的飞速发展&#xff0c;GEO&#xff08;生成式引擎优化&#xff09;已成为企业抢占AI搜索流量、提升品牌可见性的核心抓手。2025年中国GEO服务市场规模突破42亿元&#xff0c;年复合增长率达38%&#xff0c;市场上服务商良莠不齐&#xff0c;给企业选型带来挑战…

【光子AI】 FastAPI 极简教程 3

FastAPI 极简教程 文章目录 FastAPI 极简教程 目录 1. FastAPI 简介 1.1 什么是 FastAPI? 1.2 核心技术栈 2. 环境安装与配置 2.1 安装 Python 2.2 创建虚拟环境 2.3 安装 FastAPI 2.4 推荐的开发依赖 3. 第一个 FastAPI 应用 3.1 Hello World 3.2 运行应用 3.3 自动生成的文档…

《场景为王:云服务器选型的“精准匹配”指南》

《场景为王&#xff1a;云服务器选型的“精准匹配”指南》在数字化转型的浪潮中&#xff0c;云服务器已成为企业IT架构的核心载体&#xff0c;但超过50%的企业因选型不当陷入资源浪费或性能瓶颈的困境。事实上&#xff0c;不存在绝对最优的云服务器配置&#xff0c;只有与业务场…

Python操作国产金仓数据库(KingbaseES)全流程:从环境搭建到实战应用

安装必要的驱动和工具KingbaseES提供了Python的适配驱动psycopg2或kingbase-python。推荐使用psycopg2&#xff0c;因其兼容PostgreSQL协议且社区支持更广泛。通过pip安装&#xff1a;pip install psycopg2-binary若需使用官方驱动&#xff0c;需从KingbaseES官网下载kingbase-…

《打破边界:云服务器的多元形态进化史》

《打破边界&#xff1a;云服务器的多元形态进化史》当人们提及云服务器&#xff0c;往往先想到阿里云ECS、腾讯云CVM等传统虚拟机&#xff0c;但如今“云服务器”的定义已突破单一形态&#xff0c;演化出覆盖公有云、私有云、边缘计算、无服务器的多元生态。从虚拟化到容器化&a…

什么是路由监控组

文章目录为什么需要路由监控组路由监控组应用场景有哪些路由监控组是如何工作的路由监控组可以监控一定数目的路由&#xff0c;用于快速检测网络中IP路由的转发连通状况。当网络侧IP路由的转发连通状况发生变化时&#xff0c;通过IPv4静态路由与路由监控组联动&#xff0c;使接…

高通6490之完整26关节骨骼拓扑眼部跟踪集成

完整26关节骨骼拓扑 Snapdragon Spaces手部跟踪基于OpenXR标准,支持26个基本手关节(Joints[0…25]数组索引,对应XrHandJoint枚举)。额外6个辅助关节(26-31)可选使用,但核心可视化为26个。 26关节列表(标准OpenXR索引顺序) 索引 OpenXR枚举 Unity XR Hands名称 解剖位…

RAG技术详解:从原理到实践,让你轻松掌握大模型增强技术

本文全面介绍了RAG&#xff08;检索增强生成&#xff09;技术&#xff0c;包括其定义、演进历程&#xff08;Naive RAG、Advanced RAG、Modular RAG&#xff09;及工作流程。文章详细拆解了RAG的七步工作流程&#xff1a;知识分块、生成嵌入、向量存储、用户查询嵌入、语义检索…

B站用户弹幕情感可视化分析的python实现(源码+万字报告+讲解)(支持资料、图片参考_相关定制)

B站用户弹幕情感可视化分析的python实现 摘要 随着自媒体时代的兴起&#xff0c;弹幕语言在哔哩哔哩弹幕视频网站&#xff08;简称 B 站&#xff09;中的作用 不容小觑。弹幕作为一种实时评论&#xff0c;表达出了用户观看视频当下的情感与看法。本文对 B 站弹幕文本进行情感倾…

FastAPI 极简教程(偏实战,一篇搞定)

FastAPI 极简教程(偏实战,一篇搞定) 目标:用最少的概念把 FastAPI 的常用能力串起来:路由、参数、数据校验、依赖注入、错误处理、鉴权、数据库、测试、部署与 Docker。看完能直接起一个可用的小服务。 参考文档:FastAPI 官方文档、Starlette、Pydantic、Uvicorn 等(文末…

中小微企业一体化管理系统横向对比:从CRM到生产的全链路能力拆解

在数字化转型浪潮中&#xff0c;“业务孤岛”是中小微企业的核心痛点——CRM的销售数据无法同步到进销存&#xff0c;生产工单与财务核算脱节&#xff0c;薪资计算仍需人工核对销售业绩……一套能覆盖CRM、进销存、薪资、财务、上下游协同、生产工单的一体化系统&#xff0c;成…

K8S中使用 reloader 实现滚动升级

一、Reloader 核心说明&#xff08;为什么能实现滚动升级&#xff09; 1. 什么是 Reloader&#xff1f; Reloader 是 K8s 开源工具&#xff08;https://github.com/stakater/Reloader&#xff09;&#xff0c;核心功能是监听 ConfigMap/Secret 的变更&#xff0c;自动触发使用这…

大模型行业完全指南:从职位细分到产业生态,程序员必学收藏

文章系统梳理了大模型行业的完整产业链&#xff0c;从底层算力到应用落地&#xff0c;详细分析了各环节的核心职位与人才需求&#xff0c;包括算法研发、NLP、系统部署、多模态处理、语音识别及安全治理等方向。深入探讨了六大细分领域&#xff1a;训练研发、基础设施、应用产品…