优化用户体验:拦截浏览器前进后退、刷新、关闭、路由跳转等用户行为并弹窗提示
🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
需求
首先列举一下需要拦截的行为,接下来我们逐个实现。
- 浏览器前进后退
- 标签页刷新和关闭
- 路由跳转
1、拦截浏览器前进后退
这里的实现是核心,涉及到大量 History API 的理解,如果不太了解可以先看一下这两个文章:
拦截浏览器后退方法附带独家干货知识点
浏览器的History、Location对象,及使用js控制网页的前进后退和加载,刷新当前页面总结!
首先给大家明确一点,出于安全问题,浏览器并不支持通过js拦截浏览器的前进后退操作,但是可以使用障眼法。
具体思路就是我们可以在页面加载的时候,使用 history.pushState 这个API给页面添加一个当前页面的历史记录(不会导致页面刷新),此时最近的两条历史记录都是当前页面,当用户点击后退的时候,浏览器会退到上一个记录(还是当前页面),这时会触发 popstate事件 ,回退的时候再往历史记录里添加一条当前页面的记录(为了下次拦截使用),同时我们使用弹窗提示用户一些信息,如果用户确定要回退,我们再使用 history.go(-2) 跳过这两条当前页面的记录,返回到真正的上个页面,这样我们就成功模拟了回退操作的拦截。
代码实现:
import { onUnmounted } from 'vue'interface IBrowserInterceptEvents {popstate?: (next: () => void) => void // 监听浏览器前进后退
}// 作用:添加一个历史记录,以便后续模拟拦截后退
function addStopHistory() {const state = { id: 'stopBack' }if (history.state.id === 'stopBack') returnhistory.pushState(state, '', window.location.href)
}const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {const { popstate } = eventslet popstateCallback: EventListener | undefinedlet isHistoryBack = false// 拦截浏览器后退if (popstate) {addStopHistory()popstateCallback = () => {addStopHistory()popstate(() => {isHistoryBack = truehistory.go(-2)})}window.addEventListener('popstate', popstateCallback)}// 销毁事件onUnmounted(() => {// 不是历史后退触发的,仅仅是组件卸载,才需要清除模拟拦截后退时添加的历史记录if (popstate && !isHistoryBack) {history.go(-1)}popstateCallback && window.removeEventListener('popstate', popstateCallback)})
}export default useBrowserInterceptor
使用
// 使用拦截
useBrowserInterceptor({popstate: showWarnModal,
})// 弹窗提示
const showWarnModal = (next: any) => {const { pending, uploading, failed } = taskStatusMap.valueif (pending + uploading + failed > 0) {Modal.confirm({title: h('h3', '当前页面有未完成的任务!'),width: 500,content: h('div', null, [taskStatusMap.value.pending? h(Tag, { color: 'default' }, `待上传:${taskStatusMap.value.pending}`): null,taskStatusMap.value.uploading? h(Tag, { color: 'processing' }, `上传中:${taskStatusMap.value.uploading}`): null,taskStatusMap.value.failed? h(Tag, { color: 'error' }, `上传失败:${taskStatusMap.value.failed}`): null,h('div',{ style: { marginTop: '10px' } },'此操作会导致未完成上传的视频数据丢失,确定要继续吗?')]),onOk() {next()}})} else {next()}
}
2、拦截标签页刷新和关闭
这个比较简单,我们只需要监听 beforeunload 事件,阻止默认行为即可。但是这里要注意:出于浏览器安全问题,我们只能使用浏览器默认弹窗提示(如下图),无法自定义提示内容。
历史回退也有可能导致触发 beforeunload 事件,所以要添加一个 isHistoryBack 变量做判断区分。
刷新页面:
关闭页面:
代码实现
import { onUnmounted } from 'vue'interface IBrowserInterceptEvents {popstate?: (next: () => void) => void // 监听浏览器前进后退beforeunload?: EventListener // 监听标签页刷新和关闭
}// addStopHistory ...const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {const { popstate, beforeunload } = eventslet popstateCallback: EventListener | undefinedlet beforeunloadCallback: EventListener | undefinedlet isHistoryBack = false// 拦截浏览器后退 ...// 拦截标签页关闭和刷新if (beforeunload) {beforeunloadCallback = (event) => {if (!isHistoryBack) beforeunload(event)}window.addEventListener('beforeunload', beforeunloadCallback)}// 销毁事件onUnmounted(() => {// 不是后退且不是导航守卫触发的,仅仅是组件卸载,才需要清除模拟拦截后退时添加的历史记录if (popstate && !isHistoryBack) {history.go(-1)}popstateCallback && window.removeEventListener('popstate', popstateCallback)beforeunloadCallback && window.removeEventListener('beforeunload', beforeunloadCallback)})
}export default useBrowserInterceptor
使用
useBrowserInterceptor({popstate: showWarnModal,beforeunload: (e) => {const { pending, uploading, failed } = taskStatusMap.valueif (pending + uploading + failed > 0) {e.preventDefault()e.returnValue = false}}
})
3、拦截路由跳转(完整版)
这里我们可以使用 vue-router 提供的 onBeforeRouteLeave 钩子函数在组件内注册一个导航守卫,当用户跳转路由的时候进行弹窗提示。
历史回退也有可能触发导航守卫,也要使用 isHistoryBack 做判断区分。
最后我们还要处理一下事件的销毁,组件卸载时销毁事件,这里有个注意点:我们不仅要移除注册的事件,当组件卸载不是历史后退(isHistoryBack)也不是路由跳转(isRouter)触发的,仅仅是组件卸载(比如v-if),这个时候还需要清除模拟拦截后退时添加的历史记录,否则会造成页面回退异常。
代码实现(完整版)
import { onUnmounted } from 'vue'
import { type NavigationGuardNext, onBeforeRouteLeave } from 'vue-router'interface IBrowserInterceptEvents {popstate?: (next: () => void) => void // 监听浏览器前进后退beforeunload?: EventListener // 监听标签页刷新和关闭beforeRouteLeave?: (next: NavigationGuardNext) => void // 导航守卫
}// 作用:添加一个历史记录,以便后续模拟拦截后退
function addStopHistory() {const state = { id: 'stopBack' }if (history.state.id === 'stopBack') returnhistory.pushState(state, '', window.location.href)
}const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {const { popstate, beforeunload, beforeRouteLeave } = eventslet popstateCallback: EventListener | undefinedlet beforeunloadCallback: EventListener | undefinedlet isHistoryBack = falselet isRouter = false// 拦截浏览器后退if (popstate) {addStopHistory()popstateCallback = () => {addStopHistory()popstate(() => {isHistoryBack = truehistory.go(-2)})}window.addEventListener('popstate', popstateCallback)}// 拦截标签页关闭和刷新if (beforeunload) {beforeunloadCallback = (event) => {if (!isHistoryBack) beforeunload(event)}window.addEventListener('beforeunload', beforeunloadCallback)}// 导航守卫beforeRouteLeave &&onBeforeRouteLeave((_to, _from, next) => {if (isHistoryBack) {next()return}beforeRouteLeave(() => {isRouter = truenext()})})// 销毁事件onUnmounted(() => {// 不是后退且不是导航守卫触发的,仅仅是组件卸载,才需要清除模拟拦截后退时添加的历史记录if (popstate && !isHistoryBack && !isRouter) {history.go(-1)}popstateCallback && window.removeEventListener('popstate', popstateCallback)beforeunloadCallback && window.removeEventListener('beforeunload', beforeunloadCallback)})
}export default useBrowserInterceptor
使用
// 使用拦截
useBrowserInterceptor({beforeRouteLeave: showWarnModal,popstate: showWarnModal,beforeunload: (e) => {const { pending, uploading, failed } = taskStatusMap.valueif (pending + uploading + failed > 0) {e.preventDefault()e.returnValue = false}}
})// 弹窗提示
const showWarnModal = (next: any) => {const { pending, uploading, failed } = taskStatusMap.valueif (pending + uploading + failed > 0) {Modal.confirm({title: h('h3', '当前页面有未完成的任务!'),width: 500,content: h('div', null, [taskStatusMap.value.pending? h(Tag, { color: 'default' }, `待上传:${taskStatusMap.value.pending}`): null,taskStatusMap.value.uploading? h(Tag, { color: 'processing' }, `上传中:${taskStatusMap.value.uploading}`): null,taskStatusMap.value.failed? h(Tag, { color: 'error' }, `上传失败:${taskStatusMap.value.failed}`): null,h('div',{ style: { marginTop: '10px' } },'此操作会导致未完成上传的视频数据丢失,确定要继续吗?')]),onOk() {next()}})} else {next()}
}
总结
我们实现了对 用户刷新、关闭标签页、浏览器历史回退、路由跳转 等操作的拦截,可以在某些特殊场景下给用户一些友好的提示,提升用户体验。
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
相关文章:

优化用户体验:拦截浏览器前进后退、刷新、关闭、路由跳转等用户行为并弹窗提示
🧑💻 写在开头 点赞 收藏 学会🤣🤣🤣 需求 首先列举一下需要拦截的行为,接下来我们逐个实现。 浏览器前进后退标签页刷新和关闭路由跳转 1、拦截浏览器前进后退 这里的实现是核心,涉及到大…...
横川机器人驱动器导入参数教程
连接端口:有分220v和380v(刷新多次无效果就重新打开软件)升级固件:区分低压版和高压版导入参数:下载参数,下载成功后必须软重启,重新连接确认电机无干涉后相序测试 (等待10s&#x…...

大学生创新创业项目管理系统设计——数据库实验九
本实验为自己设计完成,我当年数据库实验得了94分 目录 1.实验目的 2.实验内容和要求 3.实验步骤 4.实验心得 实验九 数据库设计 1.实验目的 掌握数据库设计的过程和方法。 2.实验内容和要求 (35)大学生创新创业项目管理系统设计 一…...

电磁场与电场、磁场的关系
电磁场与电场、磁场之间存在着深刻的内在联系和统一性关系。这三者共同构成了电磁相互作用的基本框架,是理解电磁现象的关键所在。 电场和磁场实际上是电磁场的两个不同表现形式,它们既相互区别又密切联系。电场主要由静止电荷产生,表现为对…...

Python爬虫实战:研究Newspaper框架相关技术
1. 引言 1.1 研究背景与意义 互联网的快速发展使得新闻信息呈现爆炸式增长,如何高效地获取和分析这些新闻数据成为研究热点。新闻爬虫作为一种自动获取网页内容的技术工具,能够帮助用户从海量的互联网信息中提取有价值的新闻内容。本文基于 Python 的 …...

Kotlin MultiPlatform 跨平台版本的记账 App
前言 一刻记账 KMP (Kotlin MultiPlatform) 跨平台版本今天终于把 Android 和 iOS 进度拉齐了. 之前只有纯 Android 的版本. 最近大半年有空就在迁移代码到 KMP 上 中间学了 iOS 基础知识. xcode 的使用. 跨平台的架构的搭建… 感觉经历了很多很多. 一把辛酸泪 迁移的心路历…...

PIO 中的赋值魔术,MOV 指令
前言 在普通编程语言中,mov 可以理解为“赋值指令”,将一个值从一个地方拷贝到另一个地方。在 RP2040 的 PIO 汇编语言中,mov 同样是数据传递的关键指令,但它操作的是 PIO 独有的几个寄存器。 在 PIO 中,你可以用 mov …...
[docker]更新容器中镜像版本
从peccore-dev仓库拉取镜像 docker pull 10.12.135.238:8060/peccore-dev/configserver:v1.13.45如果报错,请参考docker拉取镜像失败,添加仓库地址 修改/etc/CET/Common/peccore-docker-compose.yml文件中容器的版本,为刚刚拉取的版本 # 配置中心confi…...

第十七次CCF-CSP算法(含C++源码)
第十七次CCF-CSP认证 小明种苹果AC代码 小明种苹果(续)AC代码 后面好难哈哈 小手冰凉 小明种苹果 输入输出: 题目链接 AC代码 #include<iostream> using namespace std; int n,m; int res,res3; int sum; int res21; int main(){cin …...

打造一个支持MySQL查询的MCP同步插件:Java实现
打造一个支持MySQL查询的MCP同步插件:Java实现 用Java实现一个MCP本地插件,直接通过JDBC操作本地MySQL,并通过STDIO与上层MCP客户端(例如Cursor)通信。插件注册一个名为mysql 的同步工具,接收连接参数及SQL…...

黑马k8s(十五)
1.Ingress介绍 2.Ingress使用 环境准备 Http代理 Https代理...
Axure项目实战:智慧运输平台后台管理端-订单管理1(多级交互)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:订单管理 主要内容:条件组合、中继器筛选、表单跟随菜单拖动、审批数据互通等 应用场景…...

解决 cursor 中不能进入 conda 虚拟环境
【问题】 遇到一个小问题,我创建的conda 环境在 cmd、powershell中都可以激活,但在pycharm、cursor中却不能激活? 看图 cmd中正常: cursor中不正常: 【解决方法】 cursor 中,打开终端,输入&a…...
微信小程序请求扣子(coze)api的例子
1. 准备工作 在开始之前,确保已经完成了以下准备工作: 创建并发布了 Coze 智能体。获取了个人访问令牌(Personal Access Token),这是用于授权的关键凭证。确认目标智能体的 Bot ID 和其他必要参数已准备就绪。 2. 请…...

C++ 实现二叉树的后序遍历与中序遍历构建及层次遍历输出
C 实现二叉树的后序遍历与中序遍历构建及层次遍历输出 目录 C 实现二叉树的后序遍历与中序遍历构建及层次遍历输出一、实验背景与目标二、实验环境三、实验内容四、数据结构与算法数据结构算法描述1. **构建二叉树函数 buildTree**2. **层次遍历函数 LevelOrder** 关键代码与解…...
基于大模型的髋关节骨关节炎预测与治疗方案研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 1.3 研究方法与技术路线 二、髋关节骨关节炎概述 2.1 疾病定义与分类 2.2 发病机制与病理过程 2.3 流行病学特征 三、大模型技术原理与应用基础 3.1 大模型的基本概念与架构 3.2 大模型在医疗领域的应用进展…...
qiankun解决的问题
qiankun 中的沙箱机制是如何实现的?解决了什么问题? 一、实现方式 qiankun 的沙箱机制主要用于隔离微应用之间的运行环境,避免相互影响。其核心实现基于两种策略: 快照沙箱(SnapshotSandbox) 适用于不支…...
JavaScript从入门到精通(一)
引言 JavaScript 是一种跨平台、面向对象的脚本语言,最初是为了给网页添加交互性而创建的。如今,JavaScript 不仅是浏览器端开发的核心技术,也广泛应用于服务器端(如 Node.js)、移动应用开发等多个领域。本教程旨在提…...
快速失败(fail-fast)和安全失败(fail-safe)的区别
在 Java 中,快速失败(Fail-Fast)和安全失败(Fail-Safe)是集合类(Collection)在迭代过程中处理并发修改的两种不同策略,二者的核心区别在于 对并发修改的感知机制与容错性…...

虚拟环境中的PyQt5 Pycharm设置参考
假如虚拟环境名是p3939 里面安装了pyqt5相关的库 1.QtDesigner Qt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计 位置 内容 name 可以随便命名,只要便于记忆就可以,本次采取通用…...
AI 笔记 - 模型优化 - 注意力机制在目标检测上的使用
人脸检测添加注意力机制 简介人脸检测的核心挑战与注意力机制的作用人脸检测中的注意力机制作用 选型参考基础选择(空间注意力 vs 通道注意力)空间注意力(关注“哪里”重要)通道注意力(关注“什么特征”重要࿰…...

AUTOSAR图解==>AUTOSAR_SRS_LIN
AUTOSAR LIN模块分析 目录 LIN模块概述LIN模块架构LIN通信状态流程LIN通信序列LIN配置结构总结1. LIN模块概述 本文档基于AUTOSAR规范SRS_LIN文档,对LIN(Local Interconnect Network)相关模块进行详细分析。主要包括以下几个模块: LIN接口 (LinIf)LIN驱动 (Lin)LIN传输层…...
UML 时序图 使用案例
UML 时序图 UML 时序图 (Sequence Diagram)时序图的主要元素消息类型详解时序图示例时序图绘制步骤时序图的应用场景 UML 时序图 (Sequence Diagram) 时序图是UML(统一建模语言)中用于展示对象之间交互行为的动态视图,它特别强调消息的时间顺序。 时序图的主要元素…...

华为昇腾使用ollama本地部署DeepSeek大模型
文章目录 前言一、本次使用的硬件资源二、Ollama介绍三、Ollama在arm64位的芯片的安装及使用方法总结 前言 本次打算在华为昇腾上面使用ollama进行部署DeepSeek大模型。 一、本次使用的硬件资源 存储资源 内存资源 cpu资源 二、Ollama介绍 Ollama 是一个开源的大型语言…...

多态的总结
什么是多态? 答:多态是多种形态,是为了完成某种行为时,不同对象会产生不同的形态(结合车票例子解释) 2. 什么是重载、重写(覆盖)、重定义(隐藏)? 答:重载的条件是:在同一…...

Windows 高分辨率屏幕适配指南:解决界面过小、模糊错位问题
🖥️ Windows 高分辨率屏幕适配指南:解决界面过小、模糊错位问题 摘要: 在使用高分辨率屏幕时,许多老旧的桌面软件会出现界面显示异常的问题,例如窗口过小、控件错位、文字模糊等。本文提供一套通用解决方案࿰…...
tvalid寄存器的理解
if(!out_axis_tvalid_reg || m_axis_tready ) beginend m_axis_tready 是上拍下一级给的ready信号 out_axis_tvalid_reg是上一拍,本级给下级的valid信号 一共有四种组合,然后可以通过这个if语句,在接下来的begin ... end中,用来…...
C++八股 —— 手撕定时器
文章目录 1. 什么是定时器2. 需要考虑的问题吧3. 接口设计4. 完整代码5. 性能优化 来自:腾讯百度C二面:手撕定时器_哔哩哔哩_bilibili 腾讯、网易、百度C: 手撕定时器 相关概念参考: C八股——函数对象、Lambda、bind、functi…...

K8S-statefulset-mysql-ha
需求 实现一个HA mysql,包括1个master,2个slave。在K8S上已statefulset部署。 mysql HA原理 略 K8S环境需要解决的问题 1、由于使用同一个statefulset配置,因此需要考虑master和slave使用不同的cnf文件。 2、不同pod之间文件的传输 3、…...

【方案分享】展厅智能讲解:基于BLE蓝牙Beacon的自动讲解触发技术实现
【方案分享】展厅智能讲解:基于BLE蓝牙Beacon的自动讲解触发技术实现 让观众靠近展品即可自动弹出讲解页面,是智能展厅的核心功能之一。本文将从软硬件技术、BLE Beacon原理、微信小程序实现、优劣对比与拓展方案五个维度,系统讲解“靠近展台…...