如何判斷用戶是否離開了當前頁面?
在現(xiàn)代 Web 開發(fā)中,我們常常需要知道用戶是否還停留在當前頁面。這個看似簡單的需求,背后卻關(guān)聯(lián)著用戶體驗、數(shù)據(jù)分析和系統(tǒng)性能等多個重要方面。
“離開頁面”這個行為本身可以被細分為多種場景:
- 切換到其他瀏覽器標簽頁或應(yīng)用(頁面變?yōu)椴豢梢姡搓P(guān)閉)。
 - 最小化瀏覽器窗口(同上)。
 - 關(guān)閉瀏覽器標簽頁或整個瀏覽器。
 - 在當前標簽頁中導(dǎo)航到新的 URL。
 - 在移動設(shè)備上切換到其他 App 或返回主屏幕。
 
針對這些不同的場景,前端提供了多種不同的技術(shù)和 API 來進行判斷。

方法一:Page Visibility API (頁面可見性 API) - 現(xiàn)代首選
這是處理“頁面是否對用戶可見”這一問題的標準方法。它專門用于檢測頁面是否被隱藏或顯示,非常適合處理用戶切換標簽頁、最小化窗口等場景。
核心概念:
- document.hidden:一個只讀屬性,如果頁面處于后臺或最小化狀態(tài),則返回 true,否則返回 false。
 - visibilitychange 事件:當頁面的可見性狀態(tài)發(fā)生變化時(即 document.hidden 的值改變時),該事件會在 document 對象上觸發(fā)。
 
適用場景:
- 暫停/播放視頻或音頻。
 - 停止/啟動動畫或輪播圖。
 - 暫停輪詢服務(wù)器請求,在頁面恢復(fù)可見時再繼續(xù)。
 
代碼示例:
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // 頁面變得不可見
    console.log('用戶離開了當前頁面(切換標簽頁或最小化)');
    // 在這里暫停視頻、動畫等
    pauseMyVideo();
  } else {
    // 頁面恢復(fù)可見
    console.log('用戶回到了當前頁面');
    // 在這里恢復(fù)播放
    playMyVideo();
  }
});優(yōu)點:
- 標準、可靠:W3C 標準,所有現(xiàn)代瀏覽器都支持。
 - 性能友好:專門為此設(shè)計,能有效節(jié)省 CPU 和電池資源。
 - 邏輯清晰:直接反映頁面的“可見”狀態(tài)。
 
缺點:
- 它無法判斷用戶是否正在關(guān)閉頁面。當用戶關(guān)閉標簽頁時,visibilitychange 事件可能會觸發(fā)(變?yōu)?hidden),但我們無法區(qū)分這是切換還是關(guān)閉。
 
方法二:beforeunload 和 unload 事件 - 傳統(tǒng)告別方式
這兩個事件是在用戶即將真正離開頁面(關(guān)閉、刷新、導(dǎo)航到其他鏈接)時觸發(fā)的。
1. beforeunload 事件
該事件在窗口、文檔及其資源即將被卸載時觸發(fā)。它可以用來詢問用戶是否確定要離開。
核心用途:防止用戶意外丟失未保存的數(shù)據(jù)。瀏覽器通常會彈出一個確認對話框。
代碼示例:

注意:出于安全考慮,現(xiàn)代瀏覽器不允許開發(fā)者自定義提示框中的文本內(nèi)容,只會顯示瀏覽器內(nèi)置的標準化提示。
2. unload 事件
該事件在頁面已經(jīng)開始卸載之后觸發(fā)。這是我們在用戶離開時執(zhí)行最后清理操作的傳統(tǒng)位置。
代碼示例:
window.addEventListener('unload', () => {
  console.log('用戶正在關(guān)閉或離開頁面');
  // 警告:在這里執(zhí)行的操作可能不會完成!
  // sendAnalyticsData();
});重大缺陷:unload 事件非常不可靠。瀏覽器在處理頁面卸載時,并不會等待 unload 事件處理器中的異步操作(如 fetch 或 XMLHttpRequest)完成。這意味著,如果我們想在這里發(fā)送一個分析數(shù)據(jù)到服務(wù)器,這個請求很可能在發(fā)送完成之前就被瀏覽器終止了。
方法三:navigator.sendBeacon() - 可靠的數(shù)據(jù)上報利器
為了解決 unload 事件中異步請求不可靠的問題,W3C 推出了 navigator.sendBeacon() API。
核心概念:sendBeacon() 方法可以異步地向服務(wù)器發(fā)送少量數(shù)據(jù),并且瀏覽器保證會將其啟動并排隊發(fā)送,而不會阻塞或延遲頁面的卸載過程。即使頁面已經(jīng)關(guān)閉,數(shù)據(jù)發(fā)送也會在后臺繼續(xù)進行。
適用場景:在用戶離開頁面時,可靠地發(fā)送日志、分析或統(tǒng)計數(shù)據(jù)。
如何使用(通常與 unload 或 pagehide 結(jié)合):

這種方式是目前在頁面卸載時發(fā)送數(shù)據(jù)的最佳實踐。
方法四:pagehide 和 pageshow 事件 - 應(yīng)對往返緩存(bfcache)
現(xiàn)代瀏覽器(尤其是移動端)引入了“往返緩存”(Back-Forward Cache, bfcache)。當用戶導(dǎo)航到其他頁面后,如果點擊“后退”按鈕,瀏覽器可能會直接從緩存中恢復(fù)上一個頁面,而不是重新加載它。在這種情況下,unload 事件可能根本不會觸發(fā)。
pagehide 事件則可以更好地處理這種情況。
核心概念:
- pagehide 事件:在用戶導(dǎo)航離開頁面時觸發(fā),無論頁面是否被存入 bfcache。
 - event.persisted:pagehide 事件對象的一個屬性。如果頁面被存入 bfcache,它為 true;否則為 false。
 
代碼示例:
window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('頁面正在進入 bfcache');
  } else {
    console.log('頁面正在被正常卸載');
  }
  // 無論哪種情況,這里都是發(fā)送 Beacon 的好時機
  navigator.sendBeacon('/log', getAnalyticsData());
});pagehide 比 unload 更可靠,特別是在移動設(shè)備上。因此,推薦使用 pagehide 來代替 unload。
最終建議
- 對于“可見性”判斷:優(yōu)先使用 Page Visibility API。
 - 對于“離開時上報數(shù)據(jù)”:使用 navigator.sendBeacon(),并將其放在 pagehide 事件監(jiān)聽器中,以獲得最佳的兼容性和可靠性。
 - 對于“防止數(shù)據(jù)丟失”:僅在必要時使用 beforeunload,因為它會中斷用戶操作。
 - 避免使用 unload:除非我們只需要執(zhí)行一些非常簡單的同步代碼,否則盡量避免使用它,尤其不要在其中包含異步網(wǎng)絡(luò)請求。
 
通過組合運用這些現(xiàn)代 API,我們不僅能準確地判斷用戶的行為,還能在不犧牲性能和可靠性的前提下,打造出更智能、更友好的用戶體驗。















 
 
 







 
 
 
 