什么是雙鍵緩存?我們必須了解的瀏覽器緩存新規則!
Hello,大家好,我是 Sunday。
昨天有位同學問我:“Sunday 老師,為什么我的靜態資源明明緩存了,但換個站點訪問,又得重新下載?”
這個本質上就是因為 雙鍵緩存(Double-keyed Caching) 導致的。
所以,咱們今天就來聊聊 雙鍵緩存是什么,它是如何工作的,以及我們應該如何優化?
什么是雙鍵緩存?
在 傳統的瀏覽器緩存 中,資源的緩存通常是 基于 URL 進行存儲的。
比如,當我們訪問 https://cdn.sunday.com/script.js 時,那么瀏覽器會緩存這個 script.js,當其他站點也引用這個 URL 時,瀏覽器直接復用緩存,不需要重新下載。
這種傳統的緩存方式,就是開始同學所說的:資源一旦緩存,任何站點都可以訪問
但這樣做有一個巨大的安全隱患——跨站點追蹤(Cross-site Tracking) 和 數據泄露風險。
例如:
- 某些網站可以通過檢查公共 CDN 資源的緩存狀態,來推測用戶是否訪問過某些網站(比如:廣告追蹤)。
- 黑客可以利用緩存投毒(Cache Poisoning)攻擊,讓用戶加載被污染的資源。
為了避免這些安全問題,很多瀏覽器(比如:Chrome、Firefox)引入了雙鍵緩存機制。
雙鍵緩存的核心規則是:緩存資源時,不僅考慮 URL,還要考慮 資源是在哪個站點加載的(Origin),也就是 “站點 + URL” 作為緩存的唯一標識。
換句話說:
- 以前 A 站 緩存的資源,B 站 可以直接復用 ?
- 現在 A 站 緩存的資源,B 站 需要重新下載 ?
雙鍵緩存是如何工作的?
雙鍵緩存 = 站點(Origin)+ 資源 URL
讓我們用一個例子來理解:
假設你訪問了 網頁 A 和 網頁 A,它們都使用相同的 CDN 資源 https://cdn.sunday.com/script.js:
- 傳統緩存(單鍵緩存)
你在 網頁 A 加載 script.js,瀏覽器緩存該文件。
你訪問 網頁 B,瀏覽器發現它請求相同的 script.js,于是直接從緩存中加載 (減少了網絡請求,提高了加載速度)。
- 雙鍵緩存
- 你在 網頁 A 加載 script.js,瀏覽器緩存它,并標記為 “僅供 網頁 A 使用”。
- 你訪問 網頁 B,即使請求相同的 script.js,瀏覽器也會認為它是 一個全新的資源,需要重新下載。
不同的站點,即使請求相同的資源,仍然需要分別緩存!
這種方式提升了安全性,但是也會帶來最初那位同學的疑惑,就是:資源無法跨站點共享,必須要重復下載了。
所以說:雙鍵緩存雖然帶來的“一定的”安全性,但是也帶來了不少的問題,比如:
- 緩存復用率降低:即使是相同的資源,不同站點仍然需要重新下載
- 公共 CDN 失去部分優勢:以往我們使用 CDN(如 jsDelivr、UNPKG)是為了讓多個站點共用緩存,現在效果大大降低。
- 首次訪問成本上升:用戶訪問某個站點時,即使本地已經緩存了相同的資源,仍然需要重新下載,導致頁面首次加載變慢。
如何優化雙鍵緩存影響?
在上面,咱們已經大致了解了雙鍵緩存的原理以及可能會帶來的一些影響了,所以最后咱們就來看看如何盡可能的優化這些問題:
1. 利用 Service Worker
Service Worker 可以在客戶端攔截請求,并利用 本地緩存 來減少對網絡請求的依賴。
例如,我們可以使用 Cache API 將某些資源手動緩存下來,而不受雙鍵緩存的限制:
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
Service Worker 的緩存存儲 不受雙鍵緩存的影響,因此對于高頻使用的靜態資源,可以考慮讓 Service Worker 進行管理,而不是完全依賴 HTTP 緩存。
2. 使用 HTTP/3,減少重復請求開銷
由于雙鍵緩存的影響,即使同一個用戶訪問不同網站,公共 CDN 資源也可能 多次下載。
但是,如果通過 HTTP/3(QUIC)協議 通過 多路復用 和 0-RTT 連接,可以優化對應的性能問題。
PS:如何檢查你的 CDN 是否支持 HTTP/3?
可以在 Chrome DevTools 的 Network 面板中,查看 Protocol 列,如果顯示 h3,說明該資源使用了 HTTP/3 進行傳輸。
3. 預加載關鍵資源
既然不能完全依賴瀏覽器緩存,我們可以主動 預加載關鍵資源。
例如,使用 <link rel="preload"> 來預加載字體、腳本或 CSS:
<link rel="preload" href="https://你的 cdn 地址/fonts/Roboto.woff2" as="font" type="font/woff2" crossorigin="anonymous">
這樣可以確保關鍵資源即使因為雙鍵緩存機制需要重新下載,也能更快地完成加載。