愛你的快取 ❤️

使用者第二次載入您的網站時會使用自己的 HTTP 快取,因此請確保網站可以正常運作。

本文是 2020 年 Chrome 開發人員高峰會「擴充內容」影片的系列影片,歡迎閱讀。別忘了查看影片:

使用者第二次載入您的網站時,瀏覽器會使用其 HTTP 快取內的資源來加快載入速度。但網路快取標準可追溯至 1999 年,且在廣泛界定下,判斷檔案 (例如 CSS 或圖片) 是否可能再次從網路擷取,而不是從快取載入,這有點並不精確。

在本文中,我會介紹一種可靠的現代快取預設值,也就是實際上「完全不快取」。這只是預設值,所以除了「關閉」功能以外 還有更多細微差異快繼續往下閱讀吧!

目標

如果網站第 2 次載入,您需要達成兩個目標:

  1. 確保使用者取得最新版本的可用版本。如果您變更了某些內容,系統應會迅速反映更新內容
  2. 盡量減少從網路擷取時執行 #1

最廣泛來說,您只想在客戶再次載入您的網站時,傳送最小化的變更。同時,建構網站架構,確保任何變更都能有效發布,這並非易事 (詳見下方說明)。

話雖如此,在考慮快取時,您也有其他的旋鈕,例如您決定將使用者的瀏覽器 HTTP 快取長時間保留在網站上,這樣就不會需要透過網路要求來提供快取。或者,您建構的 Service Worker 可以完全離線提供網站,再檢查是否為最新版本。這是相當有效的選項,適用於許多類似離線優先應用程式的網頁體驗,但網路不一定需要 僅快取快取的極端情況,甚至是完全僅限網路的極端選項。

背景

身為網頁程式開發人員,我們都習慣擁有「過時的快取」這個想法。 不過,我們幾乎看了就能解決這類問題的工具,例如執行「強制重新整理」動作、開啟無痕式視窗,或使用瀏覽器開發人員工具的組合來清除網站資料。

網際網路上的一般使用者 不會有同等的奢侈品因此,雖然我們訂下了一些核心目標,以確保使用者在第二次載入時有足夠的時間,但您也必須確保使用者不會遇到錯誤或遇到困難。(如果想瞭解我們是怎麼讓 web.dev/live 網站順利解決的問題,請觀看影片!)

就背景的情況而言,「過時快取」的常見原因其實是 1999 年以來的快取預設值。依賴於 Last-Modified 標頭:

這張圖表顯示使用者瀏覽器快取不同資產的時間長度
不同時間 (灰色) 產生的素材資源會按不同時間快取,因此第 2 次載入可能會同時快取和新的素材資源

您載入的每個檔案會額外保留目前生命週期的 10%,供瀏覽器查看。舉例來說,如果 index.html 是在一個月前建立,瀏覽器會在另三天後透過瀏覽器快取。

這項想法無時無刻都適用,但由於現今網站與現今網站緊密整合,這個預設行為也意味著使用者可能處於不同狀態。使用者的檔案可能是專為不同網站版本而設計 (例如星期二發布的 JS,以及星期五發布的 CSS),因為這些檔案並非即時更新。

燈火通明的小徑

現代化的快取方式,實際上基本上完全不執行任何快取,而使用 CDN 將內容更貼近使用者的位置。每次使用者載入您的網站時,都會前往網路查看網站是否是最新版本。這項要求會由靠近每位使用者的地理位置提供,因此延遲時間將較短。

您可以設定網站代管商以這個標頭回應網路要求:

Cache-Control: max-age=0,must-revalidate,public

這基本上表示這個檔案完全有效,而您必須從網路驗證檔案,才能再次使用 (否則只是「建議」)。

以傳輸的位元組數來說,這項驗證程序成本相對低廉 (如果大型圖片檔尚未變更,瀏覽器會收到較小的 304 回應),但使用者仍必須前往網路才能找到答案,因此會產生「延遲時間」。這也是我們採用此方法的主要缺點。在第 1 世界快速且快速連線的使用者,以及選擇的 CDN 可提供絕佳的涵蓋範圍。對於使用較慢行動連線或基礎架構品質不佳的使用者,這個方式也非常實用。

無論如何,這是一個現代方法,是熱門 CDN (Netlify) 的預設方法,但幾乎可以在所有 CDN 上進行設定。針對 Firebase 託管,您可以在 firebase.json 檔案的託管區段中加入這個標頭:

"headers": [
  // Be sure to put this last, to not override other headers
  {
    "source": "**",
    "headers": [ {
      "key": "Cache-Control",
      "value": "max-age=0,must-revalidate,public"
    }
  }
]

因此,雖然我仍建議將這項功能設為預設預設值,但這只是預設選項! 請參閱下文,瞭解如何執行及升級預設值。

指紋網址

透過在素材資源、圖片等網站提供的素材資源、圖片等項目名稱中加入檔案內容的雜湊,就能確保這些檔案始終擁有獨特的內容。舉例來說,這會產生名稱為 sitecode.af12de.js 的檔案。當伺服器回應這些檔案的要求時,您可以使用以下標頭設定使用者瀏覽器,安全地指示使用者的瀏覽器長期快取這些檔案:

Cache-Control: max-age=31536000,immutable

這個值是年 (秒)。根據規格,這實際上與「永遠」相同

重要的是,請不要手動產生這些雜湊,就會有太多的人工作業!您可以使用 Webpack、Rollup 等工具來協助您完成這項工作。請務必參閱工具報表瞭解詳情。

請注意,不是只有 JavaScript 能受益於使用指紋網址的網址,圖示、CSS 和其他不可變更的資料檔案等素材資源也可以用來命名。(請務必觀看上方影片,進一步瞭解程式碼分割的相關資訊,讓您每次網站變更時可減少傳送的程式碼)。

無論網站如何進行快取,這類指紋化檔案對您建立的任何網站都極具價值。大多數網站並不會每次都變更。

當然,對於向使用者顯示的頁面,我們無法以這種方式重新命名:將 index.html 檔案重新命名為 index.abcd12.html;這不但難以操作,您也無法告訴使用者每次載入網站時都會前往新的網址!您無法以這種方式重新命名及快取這些「友善」的網址,進而開啟可能的中間位置。

中間地面

在快取方面有明顯空間。我介紹了兩個極端選項,分別是快取「永不」或「永遠」。而且會有一些檔案供您長期快取,例如前面提到的「友善網址」網址。

如要快取這些「友善」的網址及其 HTML,建議您考量其中包含的依附元件、系統的快取方式,以及快取網址一次對您可能有哪些影響。我們來看一個包含如下圖片的 HTML 網頁:

<img src="/images/foo.jpeg" loading="lazy" />

如果您刪除或變更這張延遲載入的圖片,藉此更新或變更網站,則查看 HTML 快取版本的使用者可能會產生錯誤或缺少的圖片,因為使用者再次造訪您的網站時,仍會快取原始的 /images/foo.jpeg

如果仔細觀察,這可能不會對你造成影響。但最重要的是,使用者快取您的網站時,不再只存在於您的伺服器上。而是會存在使用者瀏覽器快取中的「區塊」

一般而言,快取的大部分指南都會探討這種設定,例如是否要快取 1 個小時、數小時等。如要設定這類快取,請使用像這樣的標頭 (快取時間為 3600 秒或一小時):

Cache-Control: max-age=3600,immutable,public

最後一點。如果您製作的內容通常僅限使用者存取一次 (例如新聞報導),我們看重這些內容一律不需要快取,請使用上述適當的預設設定。我想我們往往會推估使用者想隨時看到最新且最實用的內容,例如新聞報導或時事的最新重大更新,而把快取的價值高估。

非 HTML 選項

除了 HTML 以外,位於中間檔案的一些其他檔案選項包括:

  • 一般而言,請找出不會對其他項目造成影響的素材資源

    • 舉例來說,請避免使用 CSS,因為這會導致 HTML 的顯示方式改變
  • 做為即時文章的一部分顯示的大型圖片

    • 您的使用者可能不會花費太多次造訪任何報導,因此請勿永久快取相片或主頁橫幅,並浪費儲存空間
  • 代表其本身生命週期內的資產

    • 天氣相關 JSON 資料可能只每小時發布一次,因此,您可以快取一個小時的過往結果,不會在您的視窗中變更
    • 開放原始碼專案建構作業可能會受到頻率限制,因此請快取建構狀態映像檔,直到狀態可能變更為止

摘要

使用者第二次載入網站時,代表您已產生信心,他們會想要一再回訪,並進一步瞭解您提供的產品或服務。此時,不只是縮短載入時間而已,您還可以運用多種選項確保瀏覽器只執行所需工作,同時提供快速且最新的體驗。

快取不是網路上的新概念,但也可能需要合理預設值。建議您採用這種預設做法,並強烈選擇加入更佳的快取策略。感謝您閱讀本信!

另請參閱

如需 HTTP 快取的一般指南,請參閱「使用 HTTP 快取防止不必要的網路要求」。