前端專案修改了很多東西:比如bug啊,樣式啊。當你把前端專案打包之後滿心歡喜的在 Nginx(測試環境)換上它,然後在 Jira 上修改bug狀態@測試人員複測。然後測試人員開始找你battle了,你的bug怎麼還是沒修改啊,但是你明明換上了最新的版本,中間到底出現了什麼問題。開啟控制檯的 network,顯示如圖所示。
至於什麼情況下是存在記憶體,還是存在硬碟。由於計算機記憶體有限,而且比硬碟容量小很多,瀏覽器會根據計算機具體情況來決定快取放在記憶體中還是硬碟中。一般情況下,較大的 css 檔案、js 檔案和 jpg 圖片這類大檔案會被存入硬碟;當前系統記憶體使用率高的話,檔案也會被優先儲存進硬碟;而 Base64 格式的圖片,幾乎永遠可以被塞進記憶體。
那為什麼需要快取呢,對前端來說,因為讀快取不需要發起請求,也就不需要考慮請求環境和速度,提高存取速度,使用者體驗大大提升;對於後端而言,也緩解了伺服器的壓力,減少網路 IO 消耗,減少頻寬消耗。
但是什麼時候需要快取,什麼時候不需要快取。很明顯,我這種換版操作肯定是需要重新獲取新的資源的。最簡單的解決辦法就是 Ctrl+F5 強制重新整理,強制告訴瀏覽器不獲取快取,必須重新去獲取新的資源,但是強制重新整理這種手動觸發還是對使用者體驗不太友好。特別是我們做的後臺管理系統,在圖片很少的情況下,有沒有辦法每次換版之後都獲取最新的資源。這個時候就要涉及瀏覽器的快取策略了,常見的快取策略有強快取和協商快取。其實瀏覽器的快取策略都是通過設定 HTTP Header 來實現的。
強快取
不會向伺服器傳送請求,直接從快取中讀取資源。狀態碼:200,顯示 from disk cache 或 from memory cache。通過設定兩種 HTTP Header 實現:Expires和Cache-Control。
1.Expires:值是一個時間戳,表示本地時間到這個設定的時間快取就失效。這樣一來 Expires 就是有問題的,受限於本地時間,我直接手動去把電腦端的時間改掉,都能導致快取失效,所以更推薦使用 Cache-Control,或者二者搭配使用。
在 Nginx中設定寫法如下:
# gif、jpg、jpeg、png、bmp、ico這類的資源會在30天后失效 location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { root /XXXX/xxxx; expires 30d; }
2.Cache-Control:優先順序比 Expires 高,同時設定 Expires 和 Cache-Control 則後者生效。可以在請求頭或者響應頭中設定,並且可以組合使用多種指令:
在 Nginx 中設定寫法如下,隨便舉一個指令:
location ~ .*\.(css|js|swf|php|htm|html )$ { add_header Cache-Control no-store; }
協商快取
當 Cache-Control 和 Expires 過期或者它的屬性設定為 no-cache 時(即不走強快取),那麼瀏覽器第二次請求時就會與伺服器進行協商,伺服器端會對比判斷資源是否進行了修改更新,對比結果無非是以下兩種:
至於瀏覽器是怎麼和伺服器互動,主要是依靠跟協商快取相關的header頭屬性:Last-Modified/If-Modified-Since、ETag/If-None-Match。這些屬性在請求頭和響應頭是成對出現的。
1.Last-Modified/If-Modified-Since:
瀏覽器在第一次存取資源,或快取過期後存取,伺服器返回資源的同時,在 response header 中新增 Last-Modified 的 header,值是這個資源在伺服器上的最後修改時間,瀏覽器接收快取檔案和header資訊。隨後我們每次請求時,瀏覽器會自動帶上一個叫If-Modified-Since 的時間戳欄位給伺服器,它的值正是上一次 response 返回給它的 Last-modified 值,然後伺服器會根據 If-Modified-Since 的值對比資源的最後修改時間判斷資源是否進行了修改更新。
2.ETag/If-None-Match :
Etag是由伺服器為每個資源生成的唯一的標識字串,這個標識字串是基於檔案內容編碼的,只要檔案內容不同,它們對應的 Etag 就是不同的,因此 Etag 能夠精準地感知檔案的變化。Etag 和 Last-Modified 類似,當首次請求時,我們會在響應頭裡獲取到一個最初的識別符號字串。那麼下一次請求時,瀏覽器就會在請求頭裡就會帶上一個值相同的、名為 if-None-Match 的字串供伺服器比對。Etag 的優先順序會比 Last-Modified 高,但是Etag因為要生成,也會更消耗伺服器效能。
檢視 Nginx 更新紀錄檔可知,在2014年6月26日就預設開啟 Etag,對應的版本為1.7.3,也就是說1.7.3及以上的版本的 Nginx 預設開啟 Etag。不過需要注意的是,如果 Nginx 有開啟 Gzip,可能會與 Etag 有衝突。
然後就是各家的 Etag 生成情況都不太一樣,取決於伺服器的型別或設定的演演算法。以下是簡書首頁隨便的一個請求,這個不是什麼大問題,順便提一嘴。
說了這麼多,前端快取最理想的效果就是希望能儘可能多的命中強快取,對於頻繁變動的資源,多使用協商快取,同時,能在更新版本的時候讓瀏覽器的快取失效。這就要求了我們對資源進行 Nginx 設定的時候,對資源失效時間有個自己的衡量和把握。
最後,還有一種情況是瀏覽器在幾次重新整理過程中會出現新版效果,也會出現舊版效果,新舊交替。那就得考慮前端專案是否佈置了多節點,並使用 Nginx 設定負載均衡了,如果是這個問題的話,只要全部 Nginx 節點環境都換上新打的前端包問題就迎刃而解了。