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

























