瀏覽器的回上頁功能有時還蠻方便的,即使多步驟分成多個網頁的操作介面,也能按回上頁回到前一個網址修改資料再繼續,甚至連網頁捲動的位置都一樣,就像回到按送出鈕的那一刻。這讓人產生錯覺,當我們切換頁面時,瀏覽器是不是整個網頁暫存到記憶體,所以回上頁時才能恢復原狀?

用以下網頁簡單展示。

<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
    if (Request.HttpMethod == "POST")
    {
        Response.ContentType = "text/plain";
        Response.Write("SELECT: " + Request.Form["sel"]);
        Response.Write("\nCHECKBOX: " + (Request.Form["cbx"] != null ? "Checked" : "Unchecked"));
        Response.Write("\nTEXT: " + Request.Form["txt"]);
        Response.End();
    }
}

</script>
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <style>
        div {
            padding: 20px;
            border: 1px solid gray;
            margin: 1px;
        }

        .top,
        .bottom {
            background-color: #f1f1f1;
            padding: 10px;
            text-align: center;
            height: 350px;
            background-repeat: repeat;
        }
    </style>
    <script id="topSvg" type="text/template">
    <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
        <text x="50" y="50" font-size="24" font-family="Arial" fill="#cccccc" text-anchor="middle"
            dominant-baseline="middle" transform="rotate(-45 50 50)">TOP</text>
    </svg>
    </script>
    <script id="bottomSvg" type="text/template">
    <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
        <text x="50" y="50" font-size="24" font-family="Arial" fill="#cccccc" text-anchor="middle"
            dominant-baseline="middle" transform="rotate(-45 50 50)">BOTTOM</text>
    </svg>
    </script>
    <script>
        window.addEventListener('DOMContentLoaded', function () {
            ['top', 'bottom'].forEach(id => {
                const svgTemplate = document.getElementById(`${id}Svg`).textContent.trim();
                const svgBase64 = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgTemplate)));
                document.querySelector('.' + id).style.backgroundImage = 'url("' + svgBase64 + '")';
            });
        });
    </script>
</head>

<body>
    <div class="top">
    </div>
    <div>
        <form method="post">
            <select name="sel">
                <option value="1">Option 1</option>
                <option value="2">Option 2</option>
                <option value="3">Option 3</option>
            </select>
            <label>
                <input type="checkbox" name="cbx" >
                Checked
            </label>
            <input type="text" name="txt" />
            <input type="submit" value="Submit" />
        </form>
    </div>
    <div class="bottom"></div>
</body>

</html>

打開網頁向下捲動到表單欄位,下拉選單選 Option 2,勾選 Checkbox,輸入 TEST 後按 Submit 鈕,ASP.NET 正確顯示接收到的欄位值。按瀏覽器的回上頁,網頁停在送出表單時的位置,下拉選單、勾選方塊跟文字欄位都是當初輸入的值,感覺就像整個網頁被暫存在記憶體,回上頁時直接讀取還原。

但真的是這樣嗎?不是哦,不是這樣的。

稍加修改程式,在按鈕後方加上伺服器端產生的時間戳記(<% = DateTime.Now.ToString("mm:ss.fff") %>),若它改變便代表網頁被重新執行過,是瀏覽器努力讓它看起來像回到送出表單前的一刻。實測可發現,送出表單當下的時間戳為 25:34.211,顯示結果再回到上一頁,下拉選單、勾選方塊跟文字欄位的值都對,甚至連捲動位置也絲毫不差,但時間戳變成了 25.39.063,證明它不折不扣是伺服器重新生成的網頁!

依據 W3C HTML 規範 5.4 Session history and navigation,瀏覽器會在每個分頁記錄使用者瀏覽過的網頁序列,用於實現「前進」「返回」等瀏覽操作,而每個 Session History Entry 可以帶有「持久化的使用者狀態」(Persisted User State),要保存哪些狀態則由使用者代理(瀏覽器)自行決定,規範明確提示的項目包含「表單控制項的值」與「捲動位置」等。

瀏覽器確實也有不需重連網站,直接從記憶體還原網頁的機制 - bfcache。bfcache 會在離開頁面時「凍結」整個頁面(DOM、JS heap、表單值與 UI 狀態),返回時「解凍」並恢復原狀,方便 Session History Entry 以最小成本準確重現先前的互動狀態(包含表單填寫內容)。攔截 pageshow 事件,由 event.persisted == true 可偵測網頁內容是否來自 bfcache:參考

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  }
});

基於網頁包含不一樣的時間戳,可以 100% 確認我們的案例沒有啟用 bfcache,而 F12 開發工具也可檢視 bfcache 狀態及未啟用原因。(註:BrowsingInstanceNotSwapped 代表瀏覽器是基於自己的判斷決定不用 bfcache,並非 unload Event、 Cache-Control: no-store 造成)

但即便沒有啟用 bfcache,瀏覽器在重新載入或回上頁或下一頁時會嘗試由 Session History 自動恢復尚未提交的表單欄位內容,除非頁面或欄位明顯關閉自動記憶(如設定 autocomplete="off")。同時,瀏覽器也會參考 Session History 嘗試還原離開前的 Y 軸捲動位置。

結論:現代瀏覽器在重新載入或回上頁時會努力還原表單欄位值及捲動位置,可能透過 bfcache (可視為由記憶體快取直接還原狀態)或是由網站重新下載網頁再依據 Session History 保存資料恢復。這有助提供使用者更流暢的瀏覽體驗,但有時可能會引發問題,在寫前端程式時可視需求善用或避免之。

Browsers restore form values and scroll position on back navigation, even without bfcache. This improves UX, but developers should be aware of possible side effects.


Comments

# by

看著看著看到不是喔 不是這樣喔直接笑出來

Post a comment