瀏覽器的標籤頁會話機制,以及前進/後退緩存

對於瀏覽器來講,一個標籤頁就承載着一個標籤頁會話。html

標籤頁會話

本文中所講的session(會話)不是指客戶端與服務端之間的會話:web

  • 客戶端與服務端之間的會話是指:爲了完成某個目標,客戶端與服務端進行的一系列通信(請求/響應對)。在這種會話中,服務端須要經過某種機制來識別出當前的請求屬於哪個會話(好比cookie)。
  • 而本文所講的標籤頁會話是指:在一個標籤頁的生命週期中,經歷的一系列文檔替換(卸載舊的文檔,並加載新的文檔)。文檔替換過程當中會發生的事件將總結在另外一篇文章。
window.history就封裝了標籤頁內部的標籤頁會話模型。

標籤頁會話的生命週期

  1. 打開一個新的標籤頁,用戶就開始了一個新的會話。(若是有多個標籤頁同時打開,說明用戶同時處於多個會話當中)
  2. 當用戶修改標籤頁的URL,或點擊當前頁面上的超連接(且<a>target attribute爲默認值_self),或提交表單時,就會發生一次文檔替換(卸載當前document,加載新的document)。標籤頁會向會話歷史(session history)中增長一個會話歷史條目(session history entry)。windows

    若是URL的修改只是形成 hashchange(或者經過JavaScript修改了hash),也會增長一個會話歷史條目,不過不須要替換文檔了。
    若是在JavaScript中調用了 history.pushState(),也會增長一個會話歷史條目,不過不須要替換文檔了。相似地, history.replaceState()修改當前的會話歷史條目,不替換文檔。
  3. 用戶能夠經過瀏覽器的前進/後退按鈕在會話歷史條目之間遷移。大部分瀏覽器支持用戶在前進/後退按鈕上點擊鼠標右鍵,查看能夠遷移到哪些會話歷史條目。
  4. 有的瀏覽器(好比Firefox和Safari)還實現了Back-Forward Cache,從而可以更快地載入舊的會話歷史條目。
  5. 經過ctrl+shift+t,用戶可以恢復上一次關閉的標籤頁,以及它承載的會話歷史。不過,Back-Forward Cache不會隨着它的歷史條目一塊兒恢復,被恢復的歷史條目的文檔須要從新加載。

前進/後退緩存(Back-Forward Cache)

Firefox和Safari實現了Back-Forward Cache,在Webkit中它被稱爲Page Cache。關於它的詳細信息能夠查看參考資料1和2。我在這裏想指出的是,Back-Forward Cache是與標籤頁會話機制深度結合的:瀏覽器

  • 緩存時機:當標籤頁即將從一個會話歷史條目遷移到另外一個時,且須要文檔替換時(前面舉過一些不須要文檔替換的例子),若是舊的文檔知足某些條件(好比存在unloadbeforeunload的監聽器,其餘條件列舉在Using Firefox 1.5 caching - Mozilla | MDN),那麼舊的文檔不會被銷燬,而是保留在內存中並暫停其活動。
  • 恢復時機:當用戶經過瀏覽器的前進/後退按鈕來載入舊的會話歷史條目時,若是這個條目對應的文檔有被緩存,那麼直接從緩存中恢復這個文檔並恢復其活動。

Back-Forward Cache的邏輯是:用戶點擊前進/後退按鈕的時候,就是期待回到「以前看到過的那個頁面」,因此瀏覽器不須要從服務器獲取一份新的代碼並從新加載頁面。緩存

關於Back-Forward Cache的討論僅僅針對支持它的瀏覽器(Chrome不支持它)。

實驗

<!DOCTYPE html>
<!-- test2.html -->
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Test</title>
</head>

<body>
  <a href="./test3.html">link</a>
  <script>
    console.log("loading");  // 這行只會在每次從新加載的時候打印
    window.addEventListener("pageshow", function (event) {
      // 這行會在每次從新加載、從緩存中恢復的時候打印
      console.log('pageshow', event.persisted, event);
    });
  </script>
</body>

</html>
<!DOCTYPE html>
<!-- test3.html -->
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Test</title>
</head>

<body>
  <a href="./test2.html">link</a>
  <script>
    console.log("loading");  // 這行只會在每次從新加載的時候打印
    window.addEventListener("pageshow", function (event) {
      // 這行會在每次從新加載、從緩存中恢復的時候打印
      console.log('pageshow', event.persisted, event);
    });
  </script>
</body>

</html>

步驟(使用Firefox):服務器

  1. 先點擊頁面中的連接若干次,向標籤頁會話中增長曆史條目。查看控制檯,驗證載入新文檔的時候會輸出"loading"和"pageshow"。
  2. 而後經過鼠標右鍵瀏覽器前進/後退按鈕來查看前面/後面的會話歷史條目。
  3. 選擇舊的會話歷史條目載入,查看控制檯,驗證載入新文檔的時候只會輸出"pageshow"而不會輸出"loading",說明<script>並無被從新執行,而是使用先前的DOM和JavaScript環境。
  4. 關閉標籤頁,再經過ctrl+shift+t恢復上次關閉的標籤頁,驗證會話歷史條目隨着標籤頁一塊兒被恢復了。加載先前的會話歷史條目,發現文檔沒有從緩存中恢復而是從新加載,說明Back-Forward Cache在關閉標籤頁的時候被銷燬了。

參考資料

  1. Using Firefox 1.5 caching - Mozilla | MDN
  2. WebKit Page Cache I – The Basics | WebKit
  3. Working with BFCache - Archive of obsolete content | MDN
  4. 7.1 Browsing contexts - HTML Standard 定義了標籤頁、browsing context、session history、Window、Document之間的關係
  5. 7.7 Session history and navigation - HTML Standard
相關文章
相關標籤/搜索