系統介紹瀏覽器快取機制及前端優化方案

語言: CN / TW / HK

背景

image-20220610170115175

快取是用來做效能優化的好東西,但是,如果用不好快取,就會產生一系列問題:

  • 為什麼我的頁面顯示的還是老版本
  • 為什麼我的網頁白屏
  • 請重新整理下網頁
  • ...

以上問題大家或多或少都遇到過,歸根結底是使用快取的姿勢不對,今天,我們就來一起了解下瀏覽器是如何進行快取的,以及我們要怎樣科學的使用快取

瀏覽器的快取機制

1. 什麼是瀏覽器快取?

image-20220609105103551

簡單說,瀏覽器把 http 請求的資源儲存到本地,供下次使用的行為,就是瀏覽器快取

這裡先記一個點:http 響應頭,決定了瀏覽器會對資源採取什麼樣的快取策略

2. 瀏覽器是讀取快取還是請求資料?

  • 使用者第一次請求資源

image-20220609173401737

  • 整個完整流程

image-20220609171118083

3. 快取過程分類——強快取 / 協商快取

根據是否請求服務,我們把快取過程分為強快取和協商快取,也可以理解為必然經過的過程稱為強快取,如果強快取沒有,那在和伺服器協商一下

強快取

強快取看的是響應頭的 Expires 和 Cache-Control 欄位

  • Expires 是老規範,它表示的是一個絕對有效時間,在該時間之前則命中快取,如果超過則快取失效,並且,由於它是跟本地時間(可以隨意修改)做比較,會導致快取混亂
  • Cache-Control 是新規範,優先順序要高於Expires,也是目前主要使用的快取策略,欄位是max-age,表示的是一個相對時間,例如 Cache-Control: max-age=3600,代表著資源的有效期是 3600 秒。

其他配置

no-cache:需要進行協商快取,傳送請求到伺服器確認是否使用快取。

no-store:禁止使用快取,每一次都要重新請求資料。

public:可以被所有的使用者快取,包括終端使用者和 CDN 等中間代理伺服器。

private:只能被終端使用者的瀏覽器快取,不允許 CDN 等中繼快取伺服器對其快取。

協商快取

當強快取沒有命中的時候,瀏覽器會發送一個請求到伺服器,伺服器根據 header 中的部分資訊來判斷是否命中快取。如果命中,則返回 304 ,告訴瀏覽器資源未更新,可使用本地的快取。

協商快取看的是 header 中的 Last-Modified / If-Modified-Since 和 Etag / If-None-Match

快取生效,返回304,快取失效,返回200和請求結果

Etag 優先順序 Last-Modified 高

  • Last-Modified / If-Modified-Since

瀏覽器第一次請求一個資源的時候,伺服器返回的 header 中會加上 Last-Modify,Last-modify 是一個時間標識該資源的最後修改時間。

當瀏覽器再次請求該資源時,request 的請求頭中會包含 If-Modify-Since,該值為快取之前返回的 Last-Modify。伺服器收到 If-Modify-Since 後,根據資源的最後修改時間判斷是否命中快取,命中返回304使用本快取,否則返回200和請求最新資源。

  • Etag / If-None-Match

etag 是更為嚴謹的校驗,一般情況下使用時間檢驗已經足夠,但我們想象一個場景,如果我們在短暫時間內修改了服務端資源,然後又迅速的改回去,理論上這種情況本地快取還是可以繼續使用的,這就是 etag 誕生的場景。

使用 etag 時服務端會對資源進行一次類似 hash 的操作獲得一個標識(內容不變標識不變),並返回給客戶端。

再次請求時客戶端會在 If-None-Match 帶上 etag 的值給服務端進行對比驗證,如果命中返回304使用快取,否則重新請求資源。

注:由於 e-atg 服務端計算會有額外開銷,所以效能較差

擴充套件:DNS快取與CDN快取

DNS 快取

我們在網上所有的通訊行為都需要IP來進行連線,DNS解析是指通過域名獲取相應IP地址的過程。

基本上有DNS的地方就有快取,查詢順序如下:

image-20220610104424261

一般我們日常會接觸到的就是有時內網域名訪問需要修改本地host對映關係,或者某些科學上網的情況,可以通過修改本地host來正常訪問網址

CDN 快取

CDN 快取從減輕根服務的分發壓力和縮短物理的傳輸距離(跨地域訪問)上2個層面對資源訪問進行優化。

CDN節點解決了跨運營商和跨地域訪問的問題,訪問延時大大降低。

大部分請求在CDN邊緣節點完成,CDN起到了分流作用,減輕了源伺服器的負載。

一般CDN服務都由運營商提供,我們只需要瞭解如何驗證CDN是否生效即可

  • 檢視域名是否配置了CDN快取

    ping {{ 域名 }} 會看到轉向了其他地址(alikunlun)

    例如: ping customer.kukahome.com

    image-20220610110014867

  • 檢視我們的頁面資源是否命中CDN快取

通過檢視相應頭有 X-cache:HIT 欄位,則命中CDN快取,注意這裡名稱並不固定,但一般都會有HIT標識,如果是MISS 或None之類的,則沒有命中快取

image-20220610110324860

前端針對快取部署優化方案

構建演進

構建方面優化的核心思想是如何更優,更快速的載入資源,以及如何保證資源的新鮮度

這個優化過程也分為幾個階段,有些其實已經不適用現在的場景,但也可以瞭解下

  • 早期的圖示合併雪碧圖(sprite),多指令碼檔案整理到一個檔案:目的是通過減少碎片化的請求數量來加速資源載入(相關知識點是瀏覽器一般最多隻支援6個併發請求,同一時間請求數量需要控制在合理範圍)

    • 現在雪碧圖已基本被 iconfont 代替,js 載入更多采用分模組非同步載入,而不是一味合併
  • 隨著 web 應用的推廣和瀏覽器快取技術的普及,前端快取問題也隨著而來,最常見的就是服務端資源變了,但是客戶端資源始終無法更新,這個階段工程師們想了很多方案。

    • 打包時在靜態資源路徑上加上 “?v=version” 或者使用時間戳來給資原始檔命名

    • 跟 modified 快取有點像,由於時間戳並不能識別出文件內容是否變動,所以有了後來的 hash 方案,理論上 hash 出來的檔案只要內容不變,檔名就不變,大大提高了快取的使用壽命,也是現代常用打包工具的預設配置

    image-20220610141324528

  • 然後,重點來了,以上我們對 html 檔案裡連結的資源做了一系列優化,但是 html 本身也是一種靜態資源,並且,客戶在訪問頁面時是不會帶上所謂的時間戳或者版本號的,導致了很多時候雖然服務端資源更新了,但是客戶端還是用老的 html 文字發起請求,這個時候就會導致各種各樣的問題了,包括但不限於白屏,展現的舊版本頁面等等

    image-20220610150956617

    • 為了解決這個問題,目前主流的解決方案是不對 html 進行快取(一般單頁應用html檔案較小,大的是 js),只對 js,css 等靜態檔案進行本地快取

    image-20220610151923311

    • 那麼,如何讓瀏覽器不快取 html 呢,目前都是通過設定 Cache-Control實現, 有前端方案和後端方案,風險提示,前端方案很不靠譜,後端很多預設配置會覆蓋前端方案,可以做了解,生產中請使用後端配置。

      通過 html 標籤設定 cache-control

      <meta http-equiv="Pragma" content="no-cache" /> // 舊協議 <meta http-equiv="Expires" content="0" /> // 舊協議 <meta http-equiv="Cache-Control" content="no-cache" /> // 目前主流

部署配置

目前主流的前端部署方式都是使用 nginx,我們來看看 nginx 如何禁用 html 的快取

location / {   root **;   # 配置頁面不快取html和htm結尾的檔案   if ($request_filename ~* .*.(?:htm|html)$)   {       add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";   }   index index.html index.htm; }

  • Private 會影響到CDN快取命中率,但本身CDN快取也會存在版本問題,量不大的情況下也可以禁掉
  • No-cache 可以使用快取,但是使用前必須到服務端驗證,可以是 no-cache,也可以是 max-age=0
  • No-store 完全禁用快取
  • Must-revalidate 與 no-cache 類似,強制到服務端驗證,作用於一些特殊場景,例如傳送了校驗請求,但傳送失敗了之類
  • Proxy-revalidate 與上面類似,要求代理快取服務驗證有效性

以上配置可以跟據專案需要靈活配置,考慮到瀏覽器對快取協議支援會有些許差異,只是想簡單粗暴禁用 html 快取全上也沒有關係,並不會有特別大影響,除非特殊場景需要調優時需要關注。

資源壓縮

都講到這了,前端構建優化還有一個常用的就是 Gzip 資源壓縮,可以極大減小資源包體積,前端一般構建工具都有外掛支援,需要注意的是也需要 nginx 做配置才能生效

http {   gzip_static on;   gzip_proxied any; }

如果生效了,響應頭裡可以看到 content-encoding: gzip

image-20220610162843430