1、前言css
開始先扯點別的:html
估計不少前端er的同窗應該遇到過:在舊項目中添加新的功能模塊、或者修改一些靜態文件時候,當代碼部署到線上以後,需求方驗收OK,此時你送了一口氣,當你準備開始得意於本身的masterpiece時候,忽然需求方跑來和你說,不少用戶反應仍是沒有看到新的效果,或者某個圖片仍是舊的。。。。what? 估計你第一反應就是,確定是可惡的緩存搞的鬼。我遇到這樣幾種狀況;前端
一、在某個舊項目中,咱們的靜態資源部署主要是在每次更新的時候自動添加版本號的形式,好比在後面加上版本?v=時間戳,按理來講,發佈代碼以後,用戶應該拉取的是最新的資源,可是事與願違,就是有的瀏覽器直接忽略後面的版本號,獲取舊的資源;處理辦法通常是:再在靜態資源後面繼續加一個版本號再發布一次。這樣就OK了。雖然是OK了,但這就引起了一個前端代碼部署問題,看這裏:《大公司裏怎樣開發和部署前端代碼》。git
二、在某個Hybrid開發中,客戶端吊起一個webview,若是這個資源是用戶常常訪問的且這個文件常常變更(例如直播中的某次搶紅包等特效)的,須要後臺在配置這連接必須每次加上一個最新的版本號,否則大機率會有用戶訪問舊的頁面。可是這個還不必定夠,以前就遇到過,修改看版本仍是不行,明明就是緩存問題,可是你卻無能爲力,你總不能要求用戶去清緩存吧?後面硬讓運維清了cdn纔有效果,坑得不行。github
三、某個新項目中採用非覆蓋式的文件模式,即便每次有變更以後,靜態資源就會更換不一樣的hash名,這樣就能夠避免用戶訪問到舊的資源了。這也是如今比較流行的方案。web
上面講的都是本地緩存帶來的困擾,可是正是由於瀏覽器這種緩存策略,提升了web的性能,也節約了不少開銷。算法
用戶主動觸發的頁面刷新行爲(好比刷新按鈕、右鍵刷新、F5等),會致使瀏覽器放棄本地緩存,使用協商緩存(304緩存)。後端
下面講講http緩存策略。瀏覽器
2、瀏覽器緩存緩存
3、http緩存策略
一、常見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
二、設置強緩存
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指定瀏覽器每次都要去服務端檢查文件是否有更新。
Expires: 文件的絕對過時時間,在過時前,再次請求同一文件不會和服務端交互,而是直接從緩存裏取。Expires屬性的行爲受Cache-Control屬性影響,當響應頭裏同時又Cache-Control屬性,且Cache-Control屬性的值有max-age時,max-age優先級大於Expires,會重寫Expires的值。Expires由於使用絕對時間,因此它的缺點是須要客戶端和服務端保持時間同步,它的優勢是在文件過時前和服務端徹底沒有交互,對於追求性能極致的網站有很大的誘惑力。且Expires屬性是http1.0定義的,對於不支持http1.1的瀏覽器來講很寶貴。Cache-Control 的選擇更多,設置更細緻,若是同時存在的話,優先級高於 Expires。
通常的:
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 的請求沒法被緩存
三、設置協商緩存
3.一、 request中的:
If-Modify-Since: 請求頭中帶的瀏覽器緩存裏保存的上次響應時保存的文件最後修改時間
If-None-Match: 請求頭中帶的瀏覽器緩存裏保存的上次響應時保存的文件ETag
3.二、response中的:
Last-Modified: 資源的最後修改時間,注意是文件的修改時間不是建立時間
Etag: 即Entity Tag,標識一個文件特定版本的字符串,多是基於文件內容的哈希值或者是其它指紋碼,不一樣服務器實現方式不一樣
3.三、檢查更新的途徑有多種,第一種是根據文件修改時間,request帶If-Modify-Since即上次response中的Last-Modified,去服務端校驗文件是否更新;二是根據文件的ETag,request帶If-None-Match即上次response中的Etag,去服務端校驗文件是否更新。我知道你必定會問request中f-Modify-Since和If-None-Match都有的話,是知足一個服務端就返回304嗎?答案是否認的,須要二者都知足纔會返回304。主要是基於如下幾個緣由:
a、Last-Modified 標註的最後修改時間只能精確到秒,服務器都是精確到毫秒級別的。若是有些資源在一秒以內被屢次修改的話,他就不能準確標註文件的新鮮度了;
b、若是某些資源會被按期生成,當內容沒有變化,但 Last-Modified 卻改變了,致使文件沒使用緩存;
c、有可能存在服務器沒有準確獲取資源修改時間,或者與代理服務器時間不一致的情形。
四、 get請求和post請求
4.1get:
請求可被緩存;
請求保留在瀏覽器歷史記錄中
請求可被收藏爲書籤
請求不該在處理敏感數據時使用
請求有長度限制
請求只應當用於取回數據
4.2post:
請求不會被緩存
請求不會保留在瀏覽器歷史記錄中
不能被收藏爲書籤
請求對數據長度沒有要求
因此要想在客戶端作HTTP的緩存必定要注意使用GET請求!
五、維護
5.一、若是後端是有專人寫的話,緩存設置就須要他們配合,事實上,如今有專門的運維童鞋去維護這些,包括一些cdn的設置;
5.二、http緩存畢竟是web性能優化之一,也是前端er須要瞭解掌握的基礎知識之一。
3、localStorage資源緩存
以前爲了長時間緩存一些資源或者狀態有使用過localStorage,前段時間看微信公衆號的文章時候,微信把生成頁面的js都緩存到localStorage中,這算是一個比較新的緩存控制「黑科技」。
一、在快速迭代版本過程當中,咱們有時候只修改了某個js中的幾行代碼,卻須要用戶下載整個js文件,這在重視流量的移動端顯得很是浪費,mt首創的加強更新算法實現了修改多少代碼就只下載修改代碼的功能,爲用戶和公司節省大量流量
二、localstorage裏面存儲的上個版本的js內容和版本號,當本次版本號和上次版本號不一致的時候,mt拼接出增量文件url去拉取增量文件,並和上個版本的js內容合併生成新版本內容。整個方案得核心在於增量文件得計算和合並
localStrorage雖有有永久緩存大量數據的功能,可是要實現這個方案,還需考慮幾個問題:
一、版本更新機制
二、搭建更新代碼的腳手架--微信本身開發的moon.js
二、存在xss安全隱患,畢竟客戶端的代碼能夠任意修改。
一、兼容性不太好,不支持LS的瀏覽器比例仍然很大;
二、靜態資源採用LS緩存存在安全隱患;
三、非首屏的css能夠用LS緩存,首屏若是要考慮SEO,不能使用LS;
四、執行速度,讀取後使用eval或建立<script>標籤的時間會比瀏覽器直接加載慢。
五、版本控制,須要本身寫一套版本控制機制。
六、localStorage以頁面的域名劃分,而常見的靜態資源都以資源自己的域名來緩存,意味着若是你的應用有多個等價域名,它們之間的localStorage不互通,會形成緩存多份浪費。