web性能優化之--合理使用http緩存策略
一、前言
開始先扯點別的:
估計很多前端er的同學應該遇到過:在舊項目中添加新的功能模塊、或者修改一些靜態文件時候,當代碼部署到線上之後,需求方驗收OK,此時你送了一口氣,當你準備開始得意於自己的masterpiece時候,突然需求方跑來和你說,很多用戶反應還是沒有看到新的效果,或者某個圖片還是舊的。。。。what? 估計你第一反應就是,肯定是可惡的緩存搞的鬼。我遇到這樣幾種情況;
1、在某個舊項目中,我們的靜態資源部署主要是在每次更新的時候自動添加版本號的形式,比如在後面加上版本?v=時間戳,按理來說,發布代碼之後,用戶應該拉取的是最新的資源,但是事與願違,就是有的瀏覽器直接忽略後面的版本號,獲取舊的資源;處理辦法一般是:再在靜態資源後面繼續加一個版本號再發布一次。這樣就OK了。雖然是OK了,但這就引發了一個前端代碼部署問題,看這裏:《大公司裏怎樣開發和部署前端代碼》。
2、在某個Hybrid開發中,客戶端吊起一個webview,如果這個資源是用戶經常訪問的且這個文件經常變動(例如直播中的某次搶紅包等特效)的,需要後臺在配置這鏈接必須每次加上一個最新的版本號,不然大概率會有用戶訪問舊的頁面。但是這個還不一定夠,之前就遇到過,修改看版本還是不行,明明就是緩存問題,但是你卻無能為力,你總不能要求用戶去清緩存吧?後面硬讓運維清了cdn才有效果,坑得不行。
3、某個新項目中采用非覆蓋式的文件模式,即使每次有變動之後,靜態資源就會更換不同的hash名,這樣就可以避免用戶訪問到舊的資源了。這也是現在比較流行的方案。
言歸正傳,前面講的都是緩存給我們帶來的困擾,但是正是因為瀏覽器這種緩存策略,提高了web的性能,也節約了很多開銷。下面講講http緩存策略。
二、緩存策略
1、http報文組成
常見請求頭,後面還詳細分析;
Accept: text/html,image/* #瀏覽器可以接收的類型 Accept-Encoding: gzip,compress #瀏覽器可以接收壓縮編碼類型 Accept-Language: en-us,zh-cn #瀏覽器可以接收的語言和國家類型 Host: www.lks.cn:80 #瀏覽器請求的主機和端口 If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT #某個頁面緩存時間 Referer: http://www.qq.com/ #請求來自於哪個頁面 User-Agent: Mozilla/4.0 compatible; MSIE 5.5; Windows NT 5.0 #瀏覽器相關信息 Cookie: #瀏覽器暫存服務器發送的信息 Connection: close1.0/Keep-Alive1.1 #HTTP請求的版本的特點 Date: Tue, 11 Jul 2000 18:23:51GMT #請求網站的時間 Allow:GET #請求的方法 GET 常見的還有POST Keep-Alive:5 #連接的時間;5 Connection:keep-alive #是否是長連接 Cache-Control:max-age=600 #緩存的最長時間 600s
2、報文剖析
(1) request中的:
If-Modify-Since: 請求頭中帶的瀏覽器緩存裏保存的上次響應時保存的文件最後修改時間
If-None-Match: 請求頭中帶的瀏覽器緩存裏保存的上次響應時保存的文件ETag
(2) response中的:
Last-Modified: 資源的最後修改時間,註意是文件的修改時間不是創建時間
Etag: 即Entity Tag,標識一個文件特定版本的字符串,可能是基於文件內容的哈希值或者是其它指紋碼,不同服務器實現方式不同
Expires: 文件的絕對過期時間,在過期前,再次請求同一文件不會和服務端交互,而是直接從緩存裏取。Expires屬性的行為受Cache-Control屬性影響,當響應頭裏同時又Cache-Control屬性,且Cache-Control屬性的值有max-age時,max-age優先級大於Expires,會重寫Expires的值。Expires因為使用絕對時間,所以它的缺點是需要客戶端和服務端保持時間同步,它的優點是在文件過期前和服務端完全沒有交互,對於追求性能極致的網站有很大的誘惑力。且Expires屬性是http1.0定義的,對於不支持http1.1的瀏覽器來說很寶貴。Cache-Control 的選擇更多,設置更細致,如果同時存在的話,優先級高於 Expires。
Cache-Control: 緩存控制策略,值可能有public/private max-age=xxxx/no-store/no-cache。public/private定義文件是否允許中繼緩存(比如CDN)對其緩存,private僅允許瀏覽器緩存文件而不允許中繼緩存存儲文件,public都允許。max-age=xxxx/no-store/no-cache定義文件的緩存時長,max-age定義文件在指定的時間內無需去服務端檢查是否有更新,單位是秒;no-store簡單粗暴,禁止任何中繼緩存和瀏覽器存儲任何響應;no-cache指定瀏覽器每次都要去服務端檢查文件是否有更新。
檢查更新的途徑有多種,第一種是根據文件修改時間,request帶If-Modify-Since即上次response中的Last-Modified,去服務端校驗文件是否更新;二是根據文件的ETag,request帶If-None-Match即上次response中的Etag,去服務端校驗文件是否更新。我知道你一定會問request中f-Modify-Since和If-None-Match都有的話,是滿足一個服務端就返回304嗎?答案是否定的,需要兩者都滿足才會返回304。
主要是基於以下幾個原因:Last-Modified 標註的最後修改時間只能精確到秒,如果有些資源在一秒之內被多次修改的話,他就不能準確標註文件的新鮮度了
如果某些資源會被定期生成,當內容沒有變化,但 Last-Modified 卻改變了,導致文件沒使用緩存
有可能存在服務器沒有準確獲取資源修改時間,或者與代理服務器時間不一致的情形。
3、不能被緩存的請求
HTTP 信息頭中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0 等告訴瀏覽器不用緩存的請求
需要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的
經過HTTPS安全加密的請求(有人也經過測試發現,ie 其實在頭部加入 Cache-Control:max-age 信息,firefox 在頭部加入 Cache-Control:Public 之後,能夠對HTTPS的資源進行緩存)
HTTP 響應頭中不包含 Last-Modified/Etag,也不包含 Cache-Control/Expires 的請求無法被緩存
4、 get請求和post請求
get:
請求可被緩存;
請求保留在瀏覽器歷史記錄中
請求可被收藏為書簽
請求不應在處理敏感數據時使用
請求有長度限制
請求只應當用於取回數據
post:
請求不會被緩存
請求不會保留在瀏覽器歷史記錄中
不能被收藏為書簽
請求對數據長度沒有要求
所以要想在客戶端做HTTP的緩存一定要註意使用GET請求!
三、後話
1、如果後端是有專人寫的話,緩存設置就需要他們配合,事實上,現在有專門的運維童鞋去維護這些,包括一些cdn的設置;
2、http緩存畢竟是web性能優化之一,也是前端er需要了解掌握的基礎知識之一。
web性能優化之--合理使用http緩存策略