無緩存,原始模型
- 瀏覽器請求靜態(tài)資源 a.js。(請求頭:1KB)
- 服務器讀取磁盤文件 a.js忽冻,返給瀏覽器僧诚。(10KB(a.js)+1KB(響應頭) = 11KB)臀防。
- 瀏覽器再次請求,服務器又重新讀取磁盤文件 a.js搬葬,返給瀏覽器。
- 如此循環(huán)。四瘫。撵孤。
缺點:
- 浪費用戶流量。
- 浪費服務器資源平委,服務器每次都要讀磁盤文件,然后發(fā)送文件到瀏覽器奴愉。
- 瀏覽器要等待 a.js 下載并且執(zhí)行后才能渲染頁面,花費的時間會影響用戶體驗蒋荚。
有緩存,無更新
- 瀏覽器第一次請求 a.js寺酪,緩存 a.js 到本地磁盤阿趁。(1+10+1 =12KB)
- 瀏覽器再次請求 a.js,直接走瀏覽器緩存(200坠宴,from cache)角骤,不再向服務器發(fā)起請求。(0KB)
缺點:
- 如果服務器上a.jpg的文件內容變了撼短,瀏覽器每次都從緩存讀取無法獲取最新文件
有緩存熙参,Expires 更新機制
瀏覽器和服務器約定文件過期時間呛梆,瀏覽器第一次請求 a.jpg 時服務器會發(fā)送完整的文件雁刷,但是服務器在發(fā)送文件的時候還附帶發(fā)送一些額外信息——過期時間企蹭,用 Expires 字段來控制由蘑,時間是 GMT 格式的標準時間尼酿,如 Expires: Fri, 01 Jan 2021 00:00:00 GMT 裳擎。
- 瀏覽器第一次請求一個靜態(tài)資源 a.js支救。(1KB)
- 服務器把 a.js 和 a.js 的緩存過期時間(Expires:Mon, 26 Sep 2018 05:00:00 GMT)發(fā)給瀏覽器。(10+1=11KB)
- 瀏覽器接收到 a.js叉跛,同時記住了過期時間。
- 在2018年9月26日5點之前,瀏覽器再次請求 a.js风范,便不再請求服務器,直接使用上一次緩存的 a.js 文件沪么。(0KB)
- 在2018年9月26日5點01分硼婿,瀏覽器請求 a.js,發(fā)現(xiàn) a.js 緩存時間過了禽车,于是不再使用本地緩存寇漫,而是請求服務器,服務器又重新讀取磁盤文件 a.js殉摔,返給瀏覽器州胳,同時告訴瀏覽器一個新的過期時間。(1+10+1=12KB)逸月。
- 如此循環(huán)栓撞。。碗硬。
優(yōu)點:
- 在過期時間以內瓤湘,節(jié)省了用戶流量。
- 減少了服務器重復讀取磁盤文件的壓力恩尾。
- 緩存過期后弛说,能夠得到更新的 a.js 文件
缺點:
- 控制功能較為單一。緩存過期以后翰意,服務器不管 a.js有沒有變化木人,都會再次讀取 a.js文件,并返給瀏覽器冀偶。
有緩存虎囚,Expires + Last-Modified 更新機制
為了解決上個方案的問題,服務器和瀏覽器協(xié)商蔫磨,制定了一種方案淘讥,服務器每次返回 a.js 的時候,還要告訴瀏覽器 a.js 在服務器上的最近修改時間 Last-Modified (GMT標準格式)堤如。
- 瀏覽器請求 a.js 文件蒲列。(1KB)
- 服務器返回 a.js 文件(10+1=11KB)窒朋,并帶上 a.js 文件上次被修改時間 Last-Modified(GMT標準格式)以及緩存過期時間 Expires(GMT標準格式)
- 當 a.js 過期時,瀏覽器帶上 If-Modified-Since(等于上一次請求返回的 Last-Modified ) 請求服務器蝗岖。(1KB)
- 服務器比較請求頭里的 Last-Modified 時間和服務器上 a.js 文件上次被修改的時間:
- 如果一致侥猩,則告訴瀏覽器:你可以繼續(xù)用本地緩存(304)。此時抵赢,服務器不再返回 a.js 文件欺劳。(1KB)
- 如果不一致,服務器讀取磁盤上的 a.js 文件返給瀏覽器铅鲤,同時告訴瀏覽器 a.js 的最近的修改時間 Last-Modified 以及重設過期時間 Expires划提。(1+10=11KB)
- 如此循環(huán)。邢享。鹏往。
優(yōu)點:
- 緩存過期后,就算再次請求骇塘,服務器如果發(fā)現(xiàn)文件沒變化伊履,不會把 a.js 發(fā)給瀏覽器,而是告訴瀏覽器繼續(xù)使用本地緩存款违。
缺點:
- Expires 過期控制不穩(wěn)定唐瀑,因為瀏覽器端可以隨意修改時間,導致緩存使用不精準插爹。
Last-Modified 過期時間只能精確到秒哄辣。
添加 Cache-Contorl 相對時間控制
為了兼容已經(jīng)實現(xiàn)了上述方案的瀏覽器,同時加入新的緩存方案递惋,服務器除了告訴瀏覽器 Expires 柔滔,同時告訴瀏覽器一個相對時間 Cache-Control:max-age=10秒。意思是在10秒以內萍虽,使用緩存到瀏覽器的 a.js 資源睛廊。
瀏覽器先檢查 Cache-Control,如果有杉编,則以 Cache-Control 為準超全,忽略 Expires。如果沒有 Cache-Control邓馒,則以 Expires 為準嘶朱。可以看出 Cache-Control 對緩存的控制粒度更細光酣。具體參看Cache-Control MDN
添加 Etag 文件內容對比
為了解決文件修改時間只能精確到秒帶來的問題疏遏,我們給服務器引入 Etag 響應頭。也就是說 a.js 內容變了,Etag 才變财异。內容不變倘零,Etag 不變,可以理解為 Etag 是文件內容的唯一 ID戳寸。
同時引入對應的請求頭 If-None-Match呈驶,每次瀏覽器請求服務器的時候,都帶上If-None-Match字段疫鹊,該字段的值就是上次請求 a.js 時袖瞻,服務器返回給瀏覽器的 Etag。
- 瀏覽器請求 a.js拆吆。
- 服務器返回 a.js聋迎,同時告訴瀏覽器過期絕對時間(Expires)以及相對時間(Cache-Control:max-age=10),以及a.js上次修改時間Last-Modified锈拨,以及 a.js 的Etag砌庄。
- 10秒內瀏覽器再次請求 a.js羹唠,不再請求服務器奕枢,直接使用本地緩存。
- 11秒時佩微,瀏覽器再次請求 a.js缝彬,請求服務器,帶上上次修改時間 If-Modified-Since 和上次的 Etag 值 If-None-Match哺眯。
- 服務器收到瀏覽器的If-Modified-Since和If-None-Match谷浅,發(fā)現(xiàn)有If-None-Match,則比較 If-None-Match 和 服務器 a.js 文件計算后的 Etag 值奶卓,忽略If-Modified-Since的比較一疯。
a.js 文件內容沒變化,則Etag和If-None-Match 一致夺姑,服務器告訴瀏覽器繼續(xù)使用本地緩存(304)墩邀。 - 如此循環(huán)。盏浙。眉睹。
不緩存 index.html 的原因
http 緩存機制存在一個問題——瀏覽器無法主動得知服務器上的 a.js 資源變化。
不管用 Expires 還是 Cache-Control废膘,他們都只能夠控制緩存是否過期竹海,但是在緩存過期之前,瀏覽器是無法得知服務器上的資源是否變化的丐黄。只有當緩存過期后斋配,瀏覽器才會請求服務器。
想象一些我們?yōu)g覽網(wǎng)頁的場景,我們一般都是輸入網(wǎng)址艰争,訪問一個 html 文件十偶,html文件中會引入 js、css 园细、圖片等資源惦积。
我們不讓 html 文件緩存,那么每次訪問瀏覽器都會請求服務器猛频,所以瀏覽器每次都能拿到最新的 html 資源狮崩。資源更新的時候,比如 a.js 文件產(chǎn)生變動鹿寻,我們只需更改 a.js 文件資源的版本號:
<script src="http://test.com/a.js?version=0.0.1"></script>
或者在文件末尾添加 hash 值:
<script src="http://test.com/jQuery-edb203c114.10.2.js"></script>
這樣睦柴,通過設置 html 不緩存,html 引用資源內容變化則改變資源路徑的方式毡熏,就解決了無法及時得知資源更新的問題坦敌。使用webpack打包的話,借助插件可以很容易處理痢法。
與 http 緩存相關的頭信息
Expires
響應頭狱窘,代表該資源的過期時間。
如果在Cache-Control響應頭設置了 "max-age" 或者 "s-max-age" 指令财搁,那么 Expires 頭會被忽略蘸炸。
Cache-Control
請求/響應頭,通用消息頭字段尖奔,被用于在http請求和響應中搭儒,通過指定指令來實現(xiàn)緩存機制。
緩存指令是單向的提茁,這意味著在請求中設置的指令淹禾,不一定被包含在響應中。
If-Modified-Since / Last-Modified
請求頭/響應頭茴扁,資源最近修改時間铃岔,分別用于瀏覽器和服務器。
Etag
響應頭丹弱,資源標識德撬,由服務器告訴瀏覽器。
If-None-Match
請求頭躲胳,緩存資源標識蜓洪,值為 Etag ,由瀏覽器告訴服務器坯苹。