HTTP 狀態消息
923
2022-05-29
Readme
如果你已經熟悉客戶端的緩存機制,可以直接跳過,看最后一節的代碼規范。
Bug 場景
比如我們在服務器發布了一個頁面:http://domain.com/cate/page.html
在這個頁面當中我們引入了一個樣式文件 style.css
這時候產品經理告訴你,頁面需要把背景換成灰色。于是你迅速把修改后的 style.css 交給了發布,等發布的同事說OK了之后你欣喜雀躍的告訴產品:改完了!
產品打開瀏覽器,刷新了 page.html 并沒有看到修改后的效果。
于是你又不得不告訴產品說:Ctrl+F5試試?
如果這是一個移動端頁面,那就沒有辦法給產品經理的iPhone上裝一個 Ctrl 和 F5 鍵了。
問題原因
簡單來講,當你訪問一個頁面的時候,頁面引入的資源(assets)以及頁面自身都會被瀏覽器下載到你自己的電腦上。當你再次訪問同一個頁面,你電腦的瀏覽器會先從你電腦中查找上一次的“訪問痕跡”,如果有這個文件記錄,就不會去服務器請求這個文件。
頁面中的圖片,音視頻,外引js腳本、css文件都會被緩存。
當然,服務器端也會有緩存,這時候通過 Ctrl+F5 就無效了(本文不做討論)。
為什么要緩存
用戶不需要每次瀏覽都重新去服務器取文件,為服務器減輕了半數以上的壓力。
從用戶角度,大大縮短了網頁的加載時間。
緩存規則
問題來了,如果瀏覽器緩存了文件,服務器文件有了更新,怎么通知瀏覽器更新文件呢?
我們可以在 http 頭信息(header)里面設定該文件緩存的有效期。
打開 Chrome 的控制臺,查看網絡請求,我們會看到如下信息。
其中,Expires 和 Cache-Control 字段控制著這個文件在瀏覽器緩存的失效。Expires 指在這個時間點以前,這個文件緩存都是有效的。Cache-Control 中的 max-age 是說從 Date 字段的時間開始算,43200 秒以內,這個文件緩存是有效的。
Expires 是 HTTP1.0 就有的,max-age 是 HTTP1.1 提出的,所以低版本瀏覽器不支持 max-age。max-age 比 Expires 更好(因為一個時間長度比寫死一個時間點更有靈活性,也更符合“有效期”的邏輯),優先級更高。
Expires 詳情
Cache-Control 詳情
這兩個值在 html 頁面中都是可以設置的。在 html 的 meta http-equiv 中可以設置文件的頭信息。如:
我們也可以設置不緩存這個頁面,如:
后端語言在輸出 html 的時候也可以設置相應的頭信息(header)來控制緩存。下面是一個 JSP 示例:
<% response.setHeader("expires","sat,6?May?1995?12:00:00?GMT");?//將expire時間設置為一個過去時間或0,-1等response.setHeader("cache-control","no-store,no-cache,must-revalidadate");?//設置HTTP/1.1?cache-control頭response.addHeader("cache-control",?"post-check=0,pre-check=0");?//設置IE?擴展HTTP/1.1?no-cache?headerresponse.setHeader("Pragma",?"no-cache");?//設置標準HTTP/1.0?no-cache?header%>
但是如果是其他資源文件(css、img、js等),我們可以通過web服務器配置來設置,比如 Nginx 可以通過在 .conf 文件中設置 Expires。下面代碼分別為圖片設置30天緩存、為 js 和 css 設置12小時緩存:
location?~?.*\.(gif|jpg|jpeg|png|bmp|swf)$?{????expires??????30d; }location?~?.*\.(js|css)?$?{????expires??????12h; }
那么,是不是緩存過期了,瀏覽器就會去服務器重新請求一份新的文件呢?其實也不是的。當緩存過期,瀏覽器會攜帶頭信息去與服務器的該文件進行比對。如果有更新,就返回狀態碼 200,重新請求文件。如果服務器文件沒有更新,就會返回狀態碼 304,瀏覽器依舊使用緩存中的文件,這樣就不需要再從服務器下載一份 http 的主體信息(body)。
如果緩存已經過期,瀏覽器在 Request 的時候,攜帶 ETag 和 Last-Modified 與服務器的文件進行比對。
如果服務器的文件在 Last-Modified(上次修改)之后更新了服務器端的文件,就會重新下載文件主體信息,如果沒有修改,服務器返回狀態碼 304,依舊從緩存讀取文件。
ETag 是服務器資源的唯一標識符,比對的優先級高于 Last-Modified。
ETag 詳情
無論是 Last-Modified 還是 Etag,可以減少傳輸成本,但是不會減少 http 請求數,也就是說,服務器的并發數不會少。
另外,前面提到的 Cache-control 中 max-age=0 表示緩存立馬過期,請求服務器會返回 304(有緩存的前提)。Cache-Control 值為 no cache 表示總是請求服務器最新文件,不會返回 304。
狀態碼 200 是請求成功,這里不是說服務器請求成功,也包含了從緩存文件請求成功。
在 Chrome 瀏覽器,我們還會發現,狀態碼是 200 的文件,也會有 from memory cache(緩存到內存) 和 from disk cache(緩存到本地硬盤) 兩種情況:
緩存到內存(memory)中的文件,加載的時間幾乎是瞬間。
至于 Chrome 如何區分 from memory cache 和 from disk cache,目前我也不知道。
我們遇到不更新的 bug 的時候,多數是頁面外引的css和js,而頁面本身做了修改,只需要刷新一下就可以更新,并不需要 Ctrl+F5 強制刷新。
這是因為點擊瀏覽器的刷新按鈕(包括微信瀏覽器->右上角->刷新),或者新開窗口,或者地址欄敲回車,都會重新請求 html 頁面(IE8以下可能有出入,但是刷新按鈕肯定會重請求)。
用戶基本上已經習慣了刷新按鈕,所以 html 極少遇到緩存bug(除非服務器端使用緩存),但是頁面引入的其他資源就很難保證及時刷新了。
避免緩存
因為瀏覽器的 http 請求是基于 URL 來的,而且緩存機制不適用于 POST 方法,POST 是不會有緩存的。如果在請求文件的時候添加或修改攜帶的參數,瀏覽器就會認為這是一個新的文件,從而不考慮緩存,直接去請求。比如在地址欄請求新的 html 頁面:
http://domain.com/sample/page.html?201711011801
或者在引入文件的時候使用:
因為 ajax 可以設置頭信息,所以在 ajax 中:
xmlHttpRequest.setRequestHeader(“If-modified-since”,”0″);
xmlHttpRequest.setRequestHeader(“Cache-Control”,”no-cache”);
jquery 使用 ajax 方法的時候設置 cache:false。
ajax 中也可以使用隨機數參數。
response.setHeader(“Cache-Control”,”no-cache,must-revalidate”);
最后,因為瀏覽器的 http 請求是基于 URL 來的,緩存機制不適用于 POST,所以可以用 POST 代替 GET。
緩存負面影響
有時候我們打開網站會發現頁面失去了樣式,這種情況多數原因是網絡環境差(加載css失敗)或者頁面錯誤,也有極少可能是緩存在本地的 css 緩存被損壞了,瀏覽器使用了本地緩存,但是并不知道緩存已經失效,而是加載了一個無效的緩存文件。
當然,這種情況發生的概率小到可以忽略,就算發生了這種情況,我們可以把解決方案推給用戶(用戶只需要清除緩存或者使用 Ctrl+F5 強制刷新即可)。
個人觀點認為:多數網站都被建議使用緩存,如果說要挑出一個例外,那應該是適用一條法則:
更新頻率大于用戶訪問頻率
比如股票實時行情頁面,秒殺頁面……用戶對于頁面數據請求要保持最新,這樣就必須要避免數據緩存。
代碼規范
因為我們可以用強制刷新(Ctrl+F5),但是用戶可能不會。開發人員一旦習慣強制刷新之后,就很容易忽略緩存問題。所以強制刷新不是好習慣,因為不是最大限度模擬用戶行為。
在 html 中引入資源,包括媒體、CSS、JS,如果引入的資源文件是再次發布的,建議添加該文件的版本號(沒有約定版本號可以使用時間串),如:
在移動端,務必這么做。
額外的,因為 https 越來越成為主流,為了更好兼容,在使用絕對地址引入外部腳本和樣式文件,切勿限定協議類型。如:
應該替換為:
測試環境的臨時解決方案
在測試的時候,每次修改一下版本號(隨機數)都太麻煩。因為刷新按鈕可以每次都去服務器請求文件,所以我們只要將修改的文件直接放到當前瀏覽器的地址欄,然后刷新一下頁面就可以更新了(注意是和html頁面同一個瀏覽器)。移動端也可以直接打開css或js文件的URI地址刷新一下。
相關文章:微信內置瀏覽器H5如何清除緩存以及 cookie 和 localStorage 何時清除
參考資料:
H5緩存機制淺析
瀏覽器緩存機制剖析
淺談瀏覽器http的緩存機制
緩存 web前端
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。