在 Web 中判斷頁面是不是重新整理

_zhiqiu發表於2024-09-10

在 Web 開發中,我們經常需要區分使用者是否透過重新整理操作重新載入了頁面。這一操作可能是由使用者手動重新整理(如按下 F5 鍵或點選瀏覽器重新整理按鈕)或透過瀏覽器自動重新載入。判斷頁面是否重新整理有助於開發者最佳化使用者體驗,例如在使用 vue 的時候需要進行許可權控制,就需要判斷在重新整理後根據登入者的許可權去新增對應的路由。

本文將詳細解析幾種常見的判斷頁面是否重新整理的技術方案,並探討各自的適用場景、優缺點以及瀏覽器的相容性。

1. 使用 window.name

window.name 是一個持久的視窗屬性,它的值在頁面重新整理、甚至透過標籤頁導航到其他頁面時也會保留,因此可以利用它來判斷頁面是否是透過重新整理重新載入。

程式碼示例

window.onload = function() {
  if (window.name === 'isRefreshed') {
    console.log('頁面被重新整理');
  } else {
    console.log('首次載入頁面');
    window.name = 'isRefreshed';
  }
};

工作原理

  • 首次載入時,window.name 是空字串,透過設定它為 'isRefreshed' 來標記狀態。
  • 重新整理頁面後,window.name 仍保持為 'isRefreshed',因此可以判斷頁面是透過重新整理載入的。

優點

  • 簡單易用:不依賴外部儲存機制或伺服器端邏輯。
  • 跨頁面永續性:在頁面間導航時,window.name 的值依然保持,適合跨頁面場景。

缺點

  • 安全性問題window.name 的值在不同頁面間共享,可能被其他頁面讀取。
  • 手動清理:在某些場景下可能需要手動清除 window.name,例如頁面關閉時。

相容性

window.name 是一個非常老的 Web API,幾乎在所有瀏覽器中都有廣泛的支援,包括:

2. 使用 sessionStorage

sessionStorage 是 Web 儲存 API 的一部分,它為每個標籤頁維護獨立的儲存空間,並且其資料在標籤頁關閉後會被清空。我們可以利用 sessionStorage 來判斷頁面是否被重新整理:

window.onload = function() {
  if (sessionStorage.getItem('isRefreshed')) {
    console.log('頁面被重新整理');
  } else {
    console.log('首次載入頁面');
  }
  sessionStorage.setItem('isRefreshed', true);
};

工作原理

  • 當頁面首次載入時,sessionStorage 中沒有 isRefreshed 條目,因此可以判斷這是首次載入。
  • 透過設定 sessionStorage.setItem('isRefreshed', true);,標記頁面已載入。
  • 當頁面重新整理後,sessionStorage 中的 isRefreshed 條目依然存在,因此可以檢測到頁面的重新整理操作。

優點

  • 簡單且不依賴伺服器端邏輯。
  • 只對當前標籤頁有效,適合單個頁面或 SPA(單頁應用)場景。

缺點

  • 可以被人為的篡改,不能保證百分之百準確。

相容性

sessionStorage 是廣泛支援的 API,適用於以下瀏覽器:

3. 使用 performance.navigation API

瀏覽器的 performance.navigation API 提供了頁面載入的詳細資訊,包括是否是透過重新整理操作載入的頁面。透過檢查 performance.navigation.type 屬性可以判斷頁面的載入方式。

window.onload = function() {
  if (performance.navigation.type === performance.navigation.TYPE_RELOAD) {
    console.log('頁面被重新整理');
  } else {
    console.log('首次載入頁面');
  }
};

屬性解釋

  • performance.navigation.TYPE_RELOAD: 表示頁面透過重新整理載入。
  • 其他型別(如 TYPE_NAVIGATE)表示正常導航。

優點

  • 直接提供了判斷頁面重新整理與否的介面,較為精確。
  • 不需要手動儲存狀態。

缺點

  • 該 API 正在逐步棄用,未來的瀏覽器可能不會支援。
  • 不適合未來長期維護的專案,應考慮遷移到更新的 API,比如下文中的 performance.getEntriesByType

相容性

performance.navigation API 在大多數瀏覽器中都被支援,但該 API 已逐步被棄用:

4. 使用 beforeunload 事件

beforeunload 事件在使用者離開頁面之前觸發,無論是頁面重新整理、關閉還是導航到其他頁面。在此事件中,我們可以設定一個標誌位來判斷使用者是否透過重新整理離開當前頁面。

window.addEventListener('beforeunload', function() {
  localStorage.setItem('isRefreshed', 'true');
});

window.onload = function() {
  if (localStorage.getItem('isRefreshed') === 'true') {
    console.log('頁面被重新整理');
    localStorage.removeItem('isRefreshed');  // 重新整理後清除標誌位
  } else {
    console.log('首次載入頁面');
  }
};

工作原理

  • 在頁面解除安裝時(包括重新整理),透過 beforeunload 事件設定一個標誌位。
  • 頁面重新載入時,根據該標誌位判斷頁面是否透過重新整理操作載入。

優點

  • 靈活,可以處理不同型別的頁面離開操作。
  • localStorage 的資料不會在頁面關閉時清除,因此可以用於判斷跨頁面的重新整理。

缺點

  • beforeunload 事件在部分瀏覽器(尤其是移動端)可能表現不一致。
  • 如果使用者清除了瀏覽器快取或 localStorage,則無法正確判斷。

相容性

beforeunload 事件在大多數現代瀏覽器中都有廣泛支援,但可能在一些移動端瀏覽器上表現不一致:

5. 使用 performance.getEntriesByType

performance.getEntriesByType("navigation") 是一個現代 Web 效能 API,用於獲取頁面導航的詳細資訊。透過這個方法,我們可以獲取一個包含導航資訊的物件,並透過檢查該物件的 type 屬性,判斷頁面是透過重新整理載入還是其他方式進入的。

示例程式碼

window.onload = function() {
  const [navigationEntry] = performance.getEntriesByType('navigation');
  
  if (navigationEntry && navigationEntry.type === 'reload') {
    console.log('頁面被重新整理');
  } else {
    console.log('首次載入頁面');
  }
};

工作原理

  • performance.getEntriesByType('navigation') 返回一個 PerformanceNavigationTiming 物件陣列,其中包含頁面導航的詳細資訊。
  • 透過檢查 navigationEntry.type,可以確定頁面載入的型別:
    • type === 'reload': 頁面透過重新整理載入。
    • type === 'navigate': 頁面透過正常導航進入。
    • type === 'back_forward': 頁面透過瀏覽器的前進或後退按鈕載入。
    • type === 'prerender': 頁面透過預渲染載入(這個狀態通常不常見)。

優點

  • 現代性performance.getEntriesByType 是較新的 API,能夠在現代瀏覽器中準確區分頁面的導航方式。
  • 詳細資訊:除了判斷頁面重新整理,還可以獲取更多關於頁面載入效能的資料,如 DNS 解析時間、請求時間等,有助於調優頁面效能。
  • 無狀態管理:無需依賴 sessionStoragelocalStorage 等外部狀態,避免了狀態同步問題。

缺點

  • 瀏覽器相容性:雖然大多數現代瀏覽器支援此 API,但 Internet Explorer 不支援(現在已不是問題)。
  • 不適用於多次重新整理:如果需要在使用者進行多次重新整理的情況下進行追蹤,單次判斷可能不足。

使用場景

performance.getEntriesByType 適合那些只需要快速判斷頁面是否是重新整理載入的場景,並且同時有進一步效能最佳化需求的應用。對於現代 Web 開發,這是一個較為精確且無需額外儲存或會話管理的解決方案。

監控頁面載入效能示例

window.onload = function() {
  const [navigationEntry] = performance.getEntriesByType('navigation');

  if (navigationEntry) {
    console.log(`頁面載入型別: ${navigationEntry.type}`);
    console.log(`頁面載入時間: ${navigationEntry.loadEventEnd - navigationEntry.startTime} ms`);
  }
};

這種方式不僅能幫助判斷頁面載入型別,還能幫助開發者最佳化頁面效能,提供更多效能資料來分析頁面載入瓶頸。

相容性

performance.getEntriesByType 是較新的 API,在現代瀏覽器中得到廣泛支援,但較舊瀏覽器不支援:

總結

判斷頁面是否重新整理是一個常見的需求,本文介紹了五種技術方案。每種方案都有其特定的適用場景和優缺點。總結如下:

方案 優點 缺點 瀏覽器相容性
window.name 簡單、易跨頁面保持狀態 安全性問題,需手動清理 適用於所有現代瀏覽器
sessionStorage 簡單,不依賴複雜邏輯 關閉標籤頁時清空 支援現代瀏覽器及部分較舊瀏覽器
performance.navigation 直接提供頁面重新整理判斷 API 正被棄用 廣泛支援,但逐漸被廢棄
performance.getEntriesByType 精確判斷載入型別 較新,舊版瀏覽器不支援 僅支援現代瀏覽器
beforeunload 靈活,可處理多種離開頁面的操作 部分瀏覽器不支援,尤其是在移動端 大多數現代瀏覽器支援

不同的方案各有優劣,開發者應根據應用的目標使用者群體、效能需求和瀏覽器支援情況靈活選擇。如果需要簡單、跨頁面的重新整理判斷,window.name 是一個不錯的選擇;而在需要更精確、現代化的判斷方式時,performance.getEntriesByType 提供了更高的靈活性。

相關文章