優化用戶體驗:攔截瀏覽器前進后退、刷新、關閉、路由跳轉等用戶行為并彈窗提示

🧑?💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣

需求

首先列舉一下需要攔截的行為,接下來我們逐個實現。

  1. 瀏覽器前進后退
  2. 標簽頁刷新和關閉
  3. 路由跳轉

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()}
}

總結

我們實現了對 用戶刷新、關閉標簽頁、瀏覽器歷史回退、路由跳轉 等操作的攔截,可以在某些特殊場景下給用戶一些友好的提示,提升用戶體驗。

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84333.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84333.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84333.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker:容器化技術

引言 傳統部署環境逐漸不適應現在的企業開發,為了追求更加輕量,更加容易管理項目,引入了docker容器化技術去實現更加高效的部署環境。 一.docker風光下的內核功能和常用命令 1.docker容器和虛擬機的區別 我們在底層和應用層之間引入了一層do…

ping命令常用參數以及traceout命令

在網絡故障排查和性能分析中,ping和 traceroute(Windows中通常稱為 tracert)是兩個極為重要的工具。它們幫助診斷網絡連接問題,了解數據在網絡中的傳輸路徑。下面將詳細介紹這兩個命令的常用參數及其應用。 ping命令 ping命令用…

SpringBoot開發——Spring Boot異常處理全攻略:五大方案實戰對比

文章目錄 一、血淚教訓:異常處理的代價二、五大異常處理方案詳解2.1 全局異常處理(推薦方案)2.2 控制器級處理2.3 HTTP狀態碼注解2.4 ResponseEntity精細控制2.5 自定義異常體系(企業級方案)三、五大方案對比決策表四、四大避坑指南4.1 異常吞噬陷阱4.2 循環依賴問題4.3 異…

CodeBuddy 實現圖片轉素描手繪工具

本文所使用的 CodeBuddy 免費下載鏈接:騰訊云代碼助手 CodeBuddy - AI 時代的智能編程伙伴 前言 最近在社交媒體上,各種素描風格的圖片火得一塌糊涂,身邊不少朋友都在分享自己的 “素描照”,看著那些黑白線條勾勒出的獨特韻味&a…

2025.05.21華為暑期實習機考真題解析第二題

?? 點擊直達筆試專欄 ??《大廠筆試突圍》 ?? 春秋招筆試突圍在線OJ ?? 筆試突圍OJ 02. 災區物資調度路徑規劃 問題描述 在一次嚴重的自然災害后,LYA負責協調救援物資的配送工作。救援區域包含多個受災鄉鎮和一個物資集結點,各個地點之間的道路狀況各異,有些甚至…

Gartner《Optimize GenAI Strategy for 4 Key ConsumerMindsets》學習心得

一、引言 在當今數字化營銷浪潮中,生成式人工智能(GenAI)正以前所未有的速度重塑著市場格局。GenAI 既是一場充滿機遇的變革,也是一場潛在風險的挑戰。一方面,絕大多數 B2C 營銷領導者對 GenAI 賦能營銷抱有極高期待,他們看到了 GenAI 在提升時間與成本效率方面的巨大潛…

探索鏈表的奇妙世界:從基礎到高級應用

鏈表是計算機科學中一種基礎且重要的數據結構,它如同一條由珠子串成的項鏈,每個珠子(節點)都包含著數據和指向下一個珠子的線索。 與數組相比,鏈表在插入和刪除操作上更加靈活,無需預先分配固定大小的內存…

黑馬點評雙攔截器和Threadlocal實現原理

文章目錄 雙攔截器ThreadLocal實現原理 雙攔截器 實現登錄狀態刷新的原因: ? 防止用戶會話過期:通過動態刷新Token有效期,確保活躍用戶不會因固定過期時間而被強制登出 ? 提升用戶體驗:用戶無需頻繁重新登錄,只要…

Windows 中動態庫.dll 的 .lib 文件有什么作用?

在 Windows 平臺開發中, 動態鏈接庫(Dynamic Link Library, DLL)。與之相關的還有一個常讓人困惑的文件——.lib 文件。那么,這個 .lib 文件到底有什么作用呢? 一、什么是 .lib 文件? .lib 文件是 靜態導入庫(Import Library) 文件,它通常與動態鏈接庫(DLL)一起生成…

細說STM32單片機FreeRTOS消息緩沖區及其應用實例

目錄 一、消息緩沖區功能概述 二、消息緩沖區操作相關函數 1、相關函數概述 2、部分函數詳解 (1)創建消息緩沖區 (2)寫入消息 (3)讀取消息 (4)消息緩沖區狀態查詢 三、消息…

【緩存】JAVA本地緩存推薦Caffeine和Guava

🌟 引言 在軟件開發過程中,緩存是提升系統性能的常用手段。對于基礎場景,直接使用 Java集合框架(如Map/Set/List)即可滿足需求。然而,當面對更復雜的緩存場景時: 需要支持多種過期策略&#x…

IDA插件 MIPSROP的安裝和使用方法

前言 筆者的IDA版本為9.0,剛開始根據一些博客描述以為將mipsrop.py拷貝到IDA的plugins目錄即可,可操作后發現事情好像沒這么簡單,復制進去后就發現沒有博客中所說的 MIPS ROP Finder ,筆者在網上搜索了很多博客后在 https://bbs.…

(1)轉置后,行列式的值不變 (2)將行列式的任意兩行互換位置后,行列式改變符號

以下是對原始內容在不改變內容本身的前提下進行的格式優化,以提升可讀性和邏輯清晰度: ? 行列式的幾何意義 行列式(determinant)是線性代數中一個非常重要的概念,它的幾何含義可以從以下幾個方面理解: &a…

最大似然估計(Maximum Likelihood Estimation, MLE)詳解

一、定義 最大似然估計 是一種參數估計方法,其核心思想是: 選擇能使觀測數據出現概率最大的參數值作為估計值。 具體來說,假設數據 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1?,x2?,…,xn?獨立且服從某個概率分布 P ( x ∣ θ ) P(…

用go從零構建寫一個RPC(3)--異步調用+多路復用實現

在前兩個版本中,我們實現了基礎的客戶端-服務端通信、連接池、序列化等關鍵模塊。為了進一步提升吞吐量和并發性能,本版本新增了 異步發送機制 和 多路復用支持,旨在減少資源消耗、提升連接利用率。 代碼地址:https://github.com/…

FFmpeg 安裝包全攻略:gpl、lgpl、shared、master 區別詳解

這些 FFmpeg 安裝包有很多版本和變種,主要區別在于以下幾個方面: ? 一、從名稱中看出的關鍵參數: 1. 版本號 master:開發版,最新功能,但可能不穩定。n6.1 / n7.1:正式版本,更穩定…

深度學習實戰:從圖像分類到文本生成的完整案例解析

1 圖像分類案例 1.1 CIFAR10數據集介紹 cifar數據是torchvision第三方包提供的數據集 訓練集5w 測試集1w y標簽 10個類別 10分類問題 一張圖形狀 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系統服務的完整流程

[應用程序] (應用進程)│↓ 調用簡單API [SoundManager] │ ├─ 代理模式門面模式(應用進程)│ ├─ 緩存數據 ←─ 裝飾器模式(應用進程)│ └─ 轉換異常 ←─ 適配器模式(應用進程)│↓ 通過Bind…

wan2.1代碼筆記

GPU內存不夠,可以先運行umt5,然后再運行wanpipeline,參考FLUX.1代碼筆記,或者使用ComfyUI。 下面使用隨機數代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

環境搭建與工具配置

3.1 本地環境搭建 3.1.1 WAMP環境搭建漏洞靶場(一、二) WAMP(Windows Apache MySQL PHP)是搭建本地Web漏洞靶場的基礎環境。 安裝步驟: Apache:下載并安裝最新版Apache HTTP Server,配置監…