使用者報案,專案網站使用 Safari 檢視,在切換頁面時殘留載入中訊息,但使用 Chrome/IE 則一切正常。

專案網站有個主目錄網頁,點選切換其他功能網頁前會 $.blockUI 顯示"網頁載入中,請稍侯..."訊息,由於頁面很快會被新網頁取代,故沒必要關閉載入中訊息。而網頁有加
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
確保網頁不被 Cache,故使用者回到主目錄一定重新載入,不該看到載入中訊息。

程式示意如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0">
</head>
<body>
  <div class="buttons">
    <a href="/FuncA">功能A</a>  
    <a href="/FuncB">功能B</a>  
  </div>
  <script src="https://code.jquery.com/jquery-git.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.js">
  </script>
  <script>
    $(".buttons a").click(function() {
      $.blockUI({ message: "網頁載入中,請稍侯..." });
    });
  </script>
</body>
</html>

接獲報案後實測,Chrome 不管桌機或手機都沒問題,但 Safari 從功能A或功能B網頁以 history.back() 或 history.go(-1) 回到主目錄網頁確實會看到載入中訊息高掛,咦,主目錄不是已宣告網頁永不 Cache 嗎?花惹發?

爬文得到答案,這是所謂的 Back-Forward Cache (BF Cache) ,以上一頁下一頁巡覽時,瀏覽器會使用記憶體內的 Cache,記憶體內的 Cache 完整保留頁面元素渲染結果、JavaScript 變數,維持當初離開網頁的狀態。可想而知,BF Cache 可優化操作體驗,不但網頁切換迅速,還可節省不必要的網路或磁碟讀取,保留狀態也符合大部分情境的使用者預期 - 回上頁後繼續剛才未完成的操作。

然而,各家瀏覽器實作 BF Cache 的方式各有不同(這篇文章 浏览器前进-后退缓存(BF Cache) - Harttle Land 有 Chrome/Safari/Firefox 桌機與手機版的差異比較),在某些情況下瀏覽器將停用 BF Cache,以 Firefox 為例

  • 網頁註冊 unload 或 beforeunload 事件
  • 網頁宣告 Cache-Control: no-cache、Expires: 0 (<meta> 或 HTTP Header)
  • 網頁尚未載入完全
  • 涉及 indexedDB Transaction
  • 網頁中 IFrame 內嵌有不允許 Cache 的網頁

由實測結果,Safari 顯然與其他瀏覽器的行為不同,遇到 Cache-Control: no-cache 或 Expires: 0 也不會停用 BF Cache。在網頁加掛 unload 事件(function() {}空函式即可) 是一種解法,我參考 Stackoverflow 的解答,決定攔截 pageshow 事件,檢查 event.persisted 屬性偵測網頁是否來自 BF Cache,若是則呼叫 $.unblockUI() 關閉載入中訊息。(以本案例,在 pageshow 不分青紅皂白 $.unblockUI() 也能解決問題,但演練實習 persisted 的應用也好)

window.onpageshow = function(event) {
    if (event.persisted) {
        $.unblockUI();
    }
};

就醬,問題排除,而老狗又學到了新把戲~

Safari will cached no-cache webpage due to it's BF cache design, the workaround is check even.persisted to change status in pageshow event.


Comments

# by Loter

感謝這篇文章,雖然文內提到的方法我寫的網站沒辦法用,但這篇讓我找到方向解決登出後仍然能看到上一頁登出前內容的問題 乾蝦

Post a comment