緩存優(yōu)化翻車了....為啥我改了代碼,用戶還是加載舊版本?
檢查服務(wù)器,代碼確實已經(jīng)更新了,本地訪問也是最新的,可偏偏用戶那邊死活加載不到新版本。
更離譜的是,有的用戶刷新了十幾遍,頁面還是舊的!最終只能手把手教用戶清瀏覽器緩存,才勉強(qiáng)解決。
那么此時問題就來了:為什么明明已經(jīng)更新了代碼,用戶卻還在訪問舊版本?
其實這個問題就來源于那個讓咱們又愛又恨的東西 緩存!
緩存的幾種方式
想要徹底搞明白這個問題,那么咱們需要首先搞清楚 緩存到底有幾種方式
1. 強(qiáng)緩存
這是前端最常見的一種緩存策略。瀏覽器第一次請求資源后,會把它放到本地磁盤里,只要緩存時間沒過期,再次訪問時就直接讀本地文件,連請求都不會發(fā)。
比如你在響應(yīng)頭里寫了:
Cache-Control: max-age=31536000
意思就是“這個文件一年內(nèi)都有效”。這樣做的好處是:訪問速度極快、服務(wù)器毫無壓力。
但壞處也很明顯:如果你代碼更新了,而用戶緩存還沒過期,那他就永遠(yuǎn)拿不到新版本。
2. 協(xié)商緩存
為了避免強(qiáng)緩存帶來的“過期不更新”,瀏覽器和服務(wù)器還提供了一種“問一問”的機(jī)制。
流程是這樣的:瀏覽器會在請求時帶上一個標(biāo)記(文件最后修改時間 Last-Modified,或者內(nèi)容的哈希值 ETag),問服務(wù)器:“我這個版本還能不能用?”
- 如果沒變,服務(wù)器會返回
304 Not Modified
,讓瀏覽器繼續(xù)用緩存。 - 如果變了,就返回最新的文件。
協(xié)商緩存的好處是能保證更新生效,但每次請求都要跑一趟服務(wù)器,多了一點點延遲。所以一般 HTML 會用協(xié)商緩存,而 JS/CSS/圖片會用強(qiáng)緩存。
3. CDN 緩存
在大規(guī)模項目里,光靠瀏覽器緩存還不夠,通常還要加一層 CDN。
CDN 的原理是把靜態(tài)資源分發(fā)到全球各地的節(jié)點,用戶請求時,直接從離自己最近的節(jié)點拿文件。這樣速度快、延遲低,源站壓力也小。
但是,如果 CDN 緩存策略沒設(shè)置好,就可能出現(xiàn):源站更新了,CDN 節(jié)點還在提供舊文件的情況。所以要 特別注意:更新 CDN 節(jié)點緩存信息
這就是為什么有時候你明明發(fā)了新版,但全國用戶看到的都是舊版本,因為 CDN 節(jié)點沒刷新。
4. Service Worker 緩存
最后是最“高級”的方案:Service Worker。
它的本質(zhì)就是瀏覽器里的一個“代理”,完全接管了資源請求的邏輯。你可以寫成“先讀緩存再請求網(wǎng)絡(luò)”,也可以寫“先走網(wǎng)絡(luò),請求失敗了再回退緩存”。
這個機(jī)制給了我們極大的靈活性,比如離線可用、版本灰度更新。
但同時,坑也很深:如果更新邏輯寫錯,可能導(dǎo)致用戶一整年都在訪問舊版本,刷新多少次都沒用。
緩存設(shè)置的最佳實踐
那么在上面咱們已經(jīng)了解了緩存的幾種常見方案了,接下來咱們就來看下緩存設(shè)置的 最佳實踐。整體大致分為 2 部分:
1. Nginx 配置緩存策略
通常我們的靜態(tài)資源(JS、CSS、圖片)都會走長緩存,而入口 HTML 文件需要隨時更新。
所以說在 nginx 配置中。最好是針對 HTML設(shè)置 no-cache
,保證用戶刷新時能拿到新入口。
但是針對靜態(tài)資源(JS、CSS、圖片),則 設(shè)置長緩存(比如 1 年),并加上 immutable
,告訴瀏覽器“只要名字沒變,就別再去請求”
# 針對 HTML:不緩存或短緩存
location ~* \.(html)$ {
add_header Cache-Control "no-cache, must-revalidate";
}
# 針對 JS / CSS / 圖片:長緩存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|webp)$ {
add_header Cache-Control "max-age=31536000, immutable";
}
2. 增加 hash 指紋
僅僅依賴緩存配置還不夠,因為如果 JS 文件名不變,瀏覽器就算去請求也會覺得“這是舊的”。
所以構(gòu)建工具都會給文件加 hash 指紋,比如 Vite / Webpack 打包后的文件名:
index.a8f3c7.js
style.9e2b41.css
當(dāng)你修改代碼重新打包時,hash 會自動更新成:
index.7c12e9.js
style.5f8d99.css
這樣瀏覽器就會立刻加載新文件,而不是繼續(xù)命中緩存。