愛你的快取 ❤️

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

這篇文章是愛上快取影片的延伸內容,該影片是 2020 年 Chrome 開發人員大會的延伸內容。請務必觀看這部影片:

使用者第二次載入網站時,瀏覽器會使用 HTTP 快取中的資源,加快載入速度。不過,網路快取標準可追溯至 1999 年,而且定義相當廣泛,因此要判斷 CSS 或圖片等檔案是否可從網路重新擷取,而非從快取中載入,這項判斷有點不準確。

在本文中,我將介紹合理的現代化快取預設值,也就是完全「完全不執行快取」。但這只是預設值,當然比「關閉」更精細。快繼續往下閱讀吧!

目標

網站首次載入時,您有兩個目標:

  1. 確保使用者能取得最新版本,如果您有任何變更,應能快速反映
  2. 執行 #1,同時盡量減少從網路擷取的資料

從廣義來說,您只想在客戶重新載入網站時,將最小的變更傳送給客戶。想確保任何變更的分配效率都很困難 (下方和影片中更詳細介紹) 建構網站架構。

不過,考量快取時,您也能使用其他調整鈕,例如決定讓使用者的瀏覽器 HTTP 快取長時間保留您的網站,這樣一來,就不需要任何網路要求就能提供網站。或者,您已建構 Service Worker,可在檢查網站是否為最新版本之前,提供完全離線的網站。這是一個極端的選項,雖然有效,但只適用於許多離線優先應用程式類型的網頁體驗,網頁不必採用只使用快取的極端做法,甚至不必完全採用只使用網路的極端做法。

背景

身為網頁開發人員,我們都習慣「快取過期」的概念。但我們幾乎本能地知道,有哪些工具可用來解決這個問題:執行「強制重新整理」或開啟無痕式視窗,或是使用瀏覽器開發人員工具的組合,清除網站資料。

一般網際網路使用者就沒有這麼幸運。因此,雖然我們有部分核心目標,是確保使用者在第 2 次載入的情況下享有長時間的體驗,但確保使用者不會處於「劣質」或陷入困境時,同樣要注意這點。(如果想聽我談論我們如何差點讓 web.dev/live 網站卡住,請觀看影片)。

稍微說明一下背景資訊,「陳舊快取」的常見原因其實是 1999 年快取的預設值。這項技術仰賴 Last-Modified 標頭:

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

您載入的每個檔案都會保留目前壽命的 10%,以便瀏覽器查看。舉例來說,如果 index.html 是在一個月前建立,則瀏覽器也會快取這個金鑰大約三天的時間。

這在當時是個好主意,但考量到現今網站的緊密整合性,這個預設行為可能會導致使用者擁有為不同網站版本設計的檔案 (例如星期二版本的 JS 和星期五版本的 CSS),因為這些檔案並未在同一時間更新。

光線充足的路徑

現代快取的預設做法是完全不快取,並使用CDN 將內容提供給使用者。每當使用者載入您的網站時,都會前往網路查看網站是否為最新版本。這項要求會由地理位置上鄰近每位使用者的 CDN 提供,因此延遲時間較短。

您可以設定網站主機使用這個標頭回應網路要求:

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

這基本上表示檔案的有效期限為零,您必須透過網路驗證檔案,才能再次使用 (否則只是「建議」)。

就傳輸位元組而言,這個驗證程序的成本相對較低,如果大型圖片檔案未變更,瀏覽器會收到小型 304 回應,但這會產生延遲,因為使用者仍必須前往網路才能查看。這也是這種做法的主要缺點。即使是在快速連線的情況下快速連線的人也很管用,所選 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

只要你小心操作,這項政策可能不會對你造成影響。不過,請務必記住,當使用者快取網站時,網站就不再只存在於伺服器上。而是可能存在於使用者瀏覽器快取的片段中。

一般來說,大多數快取指南都會提到這類設定,例如您要快取一小時、數小時等等。如要設定這類快取,請使用類似以下的標頭 (快取時間為 3600 秒,即一小時):

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

最後一點是,如果您正在製作時效性內容,使用者通常只會存取一次 (例如新聞文章),我認為這些內容不應快取,您應使用上述合理的預設值。如果使用者希望隨時看到最新、最優質的內容,例如新聞報導或時事重大更新,我們經常會高估快取的價值。

非 HTML 選項

除了 HTML 之外,其他位於中間位置的檔案選項包括:

  • 一般來說,請尋找不會影響其他人的資產

    • 例如:避免使用 CSS,因為這會導致 HTML 呈現方式發生變更
  • 在即時文章中使用的大型圖片

    • 使用者可能不會多次造訪任何一篇文章,因此請勿永久快取相片或主圖片,以免浪費儲存空間
  • 代表本身具有生命週期的資產

    • 天氣相關的 JSON 資料可能每小時才發布一次,因此您可以將先前的結果快取一小時,因為在您指定的時間範圍內,這項資料不會變更
    • 開放原始碼專案的建構作業可能受到頻率限制,因此請快取建構狀態映像檔,直到狀態可能變更為止

摘要

使用者第二次載入網站時,就代表他們對您有信心,並且想回訪網站,進一步瞭解您提供的服務。此時,這並非只是縮短載入時間,您也能選擇多種選項,確保瀏覽器只專注於提供快速和最新使用體驗所需的工作。

快取並非網路上的新概念,但可能需要合理的預設值,不妨考慮使用這項選擇,並在需要時採用更強的快取策略。感謝您閱讀本信!

另請參閱

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