摘要:所以,今天我們就來聊一聊這個經(jīng)常被我們遺忘的瀏覽器緩存。這些被瀏覽器保存的文件就被稱為緩存。但是它的功能卻完全不同,包含了頭標簽的文件,就說明瀏覽器對于該文件緩存具有非常大的控制權(quán)。對于瀏覽器,從中似乎并不能看出是否使用了緩存。
一提起緩存,Web開發(fā)者們總是在想數(shù)據(jù)庫緩存、頁面靜態(tài)化、使用Redis內(nèi)存緩存。這些方法都有一個共性,就是集中在后臺,目的就是加快數(shù)據(jù)的讀取,少用比較容易產(chǎn)生瓶頸的部分。
后臺該優(yōu)化的都優(yōu)化到了最佳狀態(tài),卻往往疏忽了一個非常重要的過程,就是數(shù)據(jù)傳輸。想著如何快速讀取數(shù)據(jù),卻忘了如何減少請求數(shù)據(jù),或者根本不請求數(shù)據(jù)。所以,今天我們就來聊一聊這個經(jīng)常被我們遺忘的瀏覽器緩存。
認識瀏覽器緩存當瀏覽器請求一個網(wǎng)站的時候,會加載各種各樣的資源,比如HTML文檔、圖片、CSS和JS等文件。對于一些不經(jīng)常變的內(nèi)容,瀏覽器會將他們保存在本地的文件中,下次訪問相同網(wǎng)站的時候,直接加載這些資源,加速訪問。
這些被瀏覽器保存的文件就被稱為緩存。(不是指Cookie或者Localstorage)。
那么如何知曉瀏覽器是讀取了緩存還是直接請求服務(wù)器?我們就使用Segmentfault網(wǎng)站來做個示例(見下圖)。
第一次打開該網(wǎng)站后,如果再次刷新頁面。會發(fā)現(xiàn)瀏覽器加載的眾多資源中,有一部分size有具體數(shù)值,然而還有一部分請求,比如圖片、css和js等文件并沒有顯示文件大小,而是顯示了from dis cache或者from memory cache字樣。這就說明了,該資源直接從內(nèi)存或者本地硬盤直接讀取,而并沒有請求服務(wù)器。
查看緩存知道了瀏覽器從緩存中讀取文件,那么瀏覽器緩存文件存儲在哪里?以chrome為例,直接在瀏覽器地址欄輸入:chrome://cache/即可打開近期的所有緩存文件鏈接,當然你可以直接點擊打開緩存內(nèi)容。
至于背后的文件,一般存在于:C:UsersyanyingAppDataLocalGoogleChromeUser DataDefaultCache路徑中,其中yanying是你的windows用戶名稱。
緩存協(xié)商從上面的圖片可以看出。一部分請求使用了緩存,而有一部分緩存并沒有使用緩存。瀏覽器如果想判斷何時該做什么操作,就必須要有一個判定標準。這里就需要用到緩存協(xié)商。簡單來說就是Web瀏覽器和服務(wù)器之間協(xié)定一個法則,什么情況下請求資源,什么情況下不請求。
緩存協(xié)商方式和Cookie、User-Agent一樣,通過瀏覽器header進行傳輸。
緩存協(xié)商方式有3種:
Last-Modified
ETag
Expires
Last-modified 定義Last-Modified標簽代表是文件的最后修改時間,其格式是標準的GMT時間。注意: GMT是標準的格林威治時間,我們國家是GMT+8時區(qū)。所以,你看到的Last-Modified和我們的時間有8個小時差距,不過不影響使用。
一般的動態(tài)資源沒有所謂的最后修改時間。而靜態(tài)文件比如css文件、圖片等文件可以通過stat()系統(tǒng)調(diào)用獲得文件的最后修改時間。
但是,實際網(wǎng)站運行中,Web服務(wù)器(比如Apache)會自動獲取靜態(tài)資源的最后修改時間,同時會自動在HTTP頭文件中添加Last-Modified標簽。靜態(tài)資源的相應(yīng)頭文件如下圖所示:
包含了Last-Modified標簽的資源,在下次的請求中,瀏覽器會帶著該時間。當服務(wù)器接收到請求后會核對該時間后,文件是否被修改,如果修改了就直接返回數(shù)據(jù),沒有修改就直接返回304狀態(tài)碼,告知瀏覽器直接使用本地緩存。這樣,一次數(shù)據(jù)傳輸流量就被免除了,速度稍有加快。
動態(tài)資源中使用動態(tài)資源雖然沒有相對意義上的最后修改時間,但是我們還是可以直接通過發(fā)送header頭來手動定義Last-Modified。這樣,通過動態(tài)程序判斷,也可以達到靜態(tài)資源節(jié)省數(shù)據(jù)傳輸流量的作用。
這里使用PHP舉個例子:
1、首先創(chuàng)建一個php文件,發(fā)送一個Last-Modified頭標簽:
2、使用瀏覽器請求該文件,我們得到了如下的服務(wù)器返回頭:
HTTP/1.1 200 OK Date: Tue, 27 Jun 2017 15:13:02 GMT Server: Apache/2.4.9 (Win32) PHP/5.5.12 X-Powered-By: PHP/5.5.12 Last-Modified: Tue, 27 Jun 2017 15:13:02 GMT Content-Length: 0 Keep-Alive: timeout=5, max=97 Connection: Keep-Alive Content-Type: text/html觀察上面服務(wù)器返回的頭文件,包含了一個Last-Modified: Tue, 27 Jun 2017 15:13:02 GMT,這就是上面動態(tài)代碼生成的最后修改時間。
3、當我們再次請求該文件的時候,我們看下瀏覽器發(fā)送給服務(wù)器的頭文件。
GET /php/last.php HTTP/1.1 Host: localhost Connection: keep-alive Cache-Control: max-age=0 //...這里省略部分信息 If-Modified-Since: Tue, 27 Jun 2017 15:13:02 GMT觀察一下最后一行,多了一個If-Modified-Since標簽,他的時間正是服務(wù)器剛剛返回的Last-Modified的值。這個值就這樣又被返回給了服務(wù)器。
4、這樣就很簡單啦。在動態(tài)語言端(PHP)可以直接使用$_SERVER["HTTP_IF_MODIFIED_SINCE"]即可獲取時間值,接著就可以做一些簡單的對比工作。如果在這個時間之后數(shù)據(jù)沒有變化則直接返回304,告訴瀏覽器直接使用緩存,而免去數(shù)據(jù)傳輸?shù)倪^程。
而且,最終要的是。這個過程根本無需查找數(shù)據(jù)庫,所以后臺程序執(zhí)行時間非常短,從而大大減少用戶等待時間。
這樣我們就做到了動態(tài)資源也可以實現(xiàn)靜態(tài)資源的最后修改時間,從而減少數(shù)據(jù)傳輸量,達到優(yōu)化性能要求。
Etag Last-Modified缺點Last-Modified似乎已經(jīng)做到了部分性能優(yōu)化效果。但是,總是有些情況下不是很奏效。比如,一個用戶修改了一個文件,后來用戶覺得修改錯誤,于是又修改回去。
上面的過程中,文件內(nèi)容并沒有發(fā)生變化。但是,文件在系統(tǒng)中的物理最后修改時間卻發(fā)生了變化。這種情況下,如果瀏覽器再次請求資源。服務(wù)器還是會發(fā)送完整數(shù)據(jù)。從而并未完全達到我們預(yù)想的效果。
于是在此之上,我們還可以添加一個ETag標簽,用來進一步確認文件是否修改。
了解ETagETag類似于Last-Modified,也是一個header頭標簽。他的值是一串字符串,用于區(qū)分各個文件的版本信息,由于HTTP并沒有對該值做任何的格式限制,所以可以自定義生成。
ETag的值不同于Last-Modified,他并不會在文件被修改時候就發(fā)生變化,而是在文件內(nèi)容發(fā)生變化的時候才會被改變(具體什么時候改變,完全有后臺業(yè)務(wù)邏輯來判斷)。對于靜態(tài)資源,Web服務(wù)器還是會幫我們處理好這個標簽,不用考慮太多。
這里我們截取了Segmentfault的一張圖片的ETag,如下圖:
下面我們還是來討論一下動態(tài)資源模擬靜態(tài)資源發(fā)送ETag標簽的過程:
1、這里我們還是新建一個PHP文件,其中代碼是向瀏覽器發(fā)送一個包含ETag的頭文件。
2、使用瀏覽器請求該PHP文件:
看下服務(wù)器返回的header頭:
HTTP/1.1 200 OK Date: Wed, 28 Jun 2017 01:45:40 GMT Server: Apache/2.4.9 (Win64) PHP/5.5.12 X-Powered-By: PHP/5.5.12 ETag: abcd Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html里面比正常的返回多了一個ETag標簽,并且它的值就是我們剛剛設(shè)置的abcd
3、下面我們刷新瀏覽器,再次請求改頁面:
注意觀察下瀏覽器請求的頭header:
GET /etags.php HTTP/1.1 Host: localhost Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.8 Cookie: Phpstorm-65418376=dceeb07b-c7af-45d6-b8be-4079e9424244; Hm_lvt_65dfcf8f1948f7203dd3fb620de01083=1497600508; admin_id=1; admin_token=072517cddaa9c106fe4662ea70a1345c If-None-Match: abcd仔細看下最后一行,有一個If-None-Match頭標簽。該標簽的值正是我們剛剛接收到的服務(wù)器返回的ETag的值,這樣類似于Last-Modified,我們在PHP端可以使用$_SERVER["HTTP_IF_NONE_MATCH"]直接獲取我們剛剛的值。
4、獲取到該值,我們就可以直接對比文件現(xiàn)有的ETag,來決定是直接使用瀏覽器緩存還是再次發(fā)送完整數(shù)據(jù)。
小結(jié)Etag和Last-Modified非常相似,都是用來判斷一個參數(shù),從而決定是否啟用緩存。但是ETag相對于Last-Modified也有他的優(yōu)勢,他可以更加準確的判斷文件內(nèi)容是否被修改,從而在實際操作中實用程度也更高。
有了這兩種優(yōu)化方式,對于節(jié)省流量帶寬已經(jīng)起到了非常大的作用。但是總是感覺還是有點兒雞肋,畢竟每次瀏覽器還是要來詢問一下服務(wù)器,文件是否被改變。
如果,我們可以確定,一個文件在半年內(nèi)不會改變,那么我們可以讓瀏覽器在這半年時間內(nèi)都不來服務(wù)器詢問,而直接使用本地緩存。這里就需要使用第三種協(xié)商方式Expires.
ExpiresExpires這個單詞的意思是過期,在這里表示的是過期時間。它的使用方式、格式和Last-Modified一樣,都是使用瀏覽器頭,也都是標準的GMT時間。
但是它的功能卻完全不同,包含了Expires頭標簽的文件,就說明瀏覽器對于該文件緩存具有非常大的控制權(quán)。例如,一個文件的Expires值是2020年的1月1日,那么就代表,在2020年1月1日之前,瀏覽器都可以直接使用該文件的本地緩存文件,而不必去服務(wù)器再次請求該文件,哪怕服務(wù)器文件發(fā)生了變化。
所以,Expires是優(yōu)化中最理想的情況,因為它根本不會產(chǎn)生請求,所以后端也就無需考慮查詢快慢。
下面我們看下segmentfault的靜態(tài)文件的Expires:
對于靜態(tài)資源,大多數(shù)服務(wù)器是會開啟expires標記功能。如果遇到?jīng)]有開啟的,則可以使用配置文件開啟。
Apache的expires支持設(shè)置如下:
ExpiresActive on ExpiresByType image/gif "access plus 1 month" ExpiresByType text/css "now plus 2 day" ExpiresDefault "now plus 1 day" 上面的配置中我們設(shè)置image/gif的格式圖片緩存時間為1個月,而css文件緩存時間為2天,其他的默認為1天。
另外,對于常用靜態(tài)資源。如果不在web服務(wù)器端設(shè)置expires標簽,瀏覽器也可以智能的標記一個過期時間。比如gif圖片,瀏覽器會設(shè)置他的過期時間為永不過期。
動態(tài)資源中使用Expires這里我們還是拿PHP來舉例
1、首先創(chuàng)建一個PHP文件,用于發(fā)送Expires頭標簽。
這里我們把文件過期時間直接設(shè)置為2020年1月1日的0點
2、使用瀏覽器請求該文件,觀察服務(wù)器返回的頭文件:
HTTP/1.1 200 OK Date: Wed, 28 Jun 2017 02:24:18 GMT Server: Apache/2.4.9 (Win64) PHP/5.5.12 X-Powered-By: PHP/5.5.12 Expires: Tue, 31 Dec 2019 16:00:00 GMT Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html不出意外,我們已經(jīng)在頭文件里面發(fā)現(xiàn)Expires標簽,并且它的值為Tue, 31 Dec 2019 16:00:00 GMT(這里不是2020年原因是由于有8個小時時差)。
3、再次使用瀏覽器訪問改頁面,發(fā)現(xiàn)瀏覽器的請求數(shù)據(jù)的路徑已經(jīng)變?yōu)榱?b>from cache(如下圖)。
對于chrome瀏覽器,從network中似乎并不能看出是否使用了緩存。但是,如果打開chrome://cache/,搜索我們剛剛的地址,會發(fā)現(xiàn)我們請求的內(nèi)容也被緩存成功。
請求方式與緩存瀏覽器有3種請求服務(wù)器資源的方式
ctrl+f5:強制刷新
f5:刷新頁面
瀏覽器地址欄回車,也就是轉(zhuǎn)到功能
這3種請求方式對于資源使用緩存的影響各不不同,下面一一的解釋:
1、ctrl + f5:強制刷新這種方式是所有加載方式中使用緩存最少的方式。當使用ctrl+f5訪問一個地址的時候,瀏覽器會強制所有的資源重新加載一次。所有的資源將會被重新緩存。
2、f5:刷新頁面f5刷新頁面相當于瀏覽器上面的刷新按鈕,是一種比較常用的刷新方式。這種方式下瀏覽器會使用部分必要的緩存,針對于Last-Modified有效,但是expires標簽就會失去他的作用。
3、地址欄轉(zhuǎn)到方式在瀏覽器地址欄輸入即將訪問的地址后,按回車或者瀏覽器轉(zhuǎn)到功能訪問網(wǎng)頁。這是使用最多的一種情況,也是使用最少請求服務(wù)器的方式。也就說瀏覽器會盡量使用本地緩存,而避免直接請求服務(wù)器數(shù)據(jù)。注意: Expires標簽也只有在這種情況下有效。所以,千萬不要使用f5或者ctrl+f5還奇怪expires功能無效。
cache-control 還有一點點小缺陷了解了上面所有的緩存協(xié)商方式后,我們已經(jīng)可以高效的優(yōu)化我們現(xiàn)有的應(yīng)用。但是還是存在一種可能情況,那就是之前的Last-Modified和expires都是使用服務(wù)器標準時間來標記。
而作為最后的判斷者確是瀏覽器。所以,難免會存在用戶電腦時間和服務(wù)器時間不一致的情況。
比如我們設(shè)定一個資源在未來10分鐘內(nèi)不會過期,而用戶電腦比服務(wù)器時間快了1個小時(當然這個太少見)。那么我們設(shè)置的過期時間對于用戶來講,立即就過期了。那么我們的設(shè)置相當于白用功了。
所以為了解決這個可能出現(xiàn)的小缺陷,我們還可以設(shè)置一個相對于用戶本地時間的緩存過期時間cache-control。
作用cache-control和之前的Last-Modified一樣,都是頭文件里面的一個標簽。只不過他的值是max-age=
,這里的 是一個數(shù)字,單位為秒。 假設(shè)我們設(shè)置一個值cahce-control:max-age=3600,那么就代表改緩存有效期是用戶本地時間加上3600秒。這樣,緩存的截止時間就和服務(wù)器時間沒有太大關(guān)系了,從而避免了因為時間偏差帶來的不良影響。
對于靜態(tài)文件,如果服務(wù)器比如Apache開啟了expires功能,那么也會默認的給頭文件添加一個cache-control標簽。
PHP設(shè)置cache-control對于動態(tài)文件,我們可以在程序語言中向瀏覽器直接輸出該標簽。我們使用PHP做一個演示:
1、創(chuàng)建一個PHP文件,向瀏覽器輸出一個包含cache-control標簽的頭:
2、使用瀏覽器請求該PHP文件,獲取服務(wù)器返回頭header:
HTTP/1.1 200 OK Date: Wed, 28 Jun 2017 12:33:16 GMT Server: Apache/2.4.9 (Win32) PHP/5.5.12 X-Powered-By: PHP/5.5.12 Cache-Control: max-age=3600 Content-Length: 0 Keep-Alive: timeout=5, max=98 Connection: Keep-Alive Content-Type: text/html觀察上面的信息,可以發(fā)現(xiàn)其中包含cache-control標簽,其值為我們剛剛設(shè)置的max-age=3600,那么就代表相對于我本地時間3600秒之后緩存過期。(完)
嚴穎,2017.6.28
個人主頁:segmentfault
推薦一個我們團隊自己開發(fā)的針對開發(fā)者的網(wǎng)址導(dǎo)航:筆點導(dǎo)航 - 用心做最簡潔的網(wǎng)址導(dǎo)航
可以自定義網(wǎng)址
可以自定義分類
分類可以標記顏色
自定義皮膚
自定義搜索
網(wǎng)址拖拽排序
自定義插件小模塊
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/61863.html
摘要:前端日報精選如何在非項目中使用知乎專欄編碼規(guī)范最常被遺忘的性能優(yōu)化瀏覽器緩存?zhèn)€人文章譯統(tǒng)一樣式語言掘金新的開發(fā)者提及最多的個視頻眾成翻譯中文第期在中使用譯統(tǒng)一樣式語言掘金前端現(xiàn)狀答題救不了前端新人相學(xué)長懟前端歲以 2017-06-29 前端日報 精選 如何在非 React 項目中使用 Redux - 知乎專欄Javascript編碼規(guī)范 - Clearlove - SegmentFau...
摘要:每個分值,函數(shù)就會刪除所有排名在名之后的商品,并將刪除之后剩余的所有商品的瀏覽次數(shù)減半。上一篇文章實戰(zhàn)第二章使用構(gòu)建應(yīng)用第四節(jié)數(shù)據(jù)行緩存下一篇文章實戰(zhàn)第三章命令第一節(jié)字符串 上一篇文章:Python--Redis實戰(zhàn):第二章:使用Redis構(gòu)建Web應(yīng)用:第四節(jié):數(shù)據(jù)行緩存下一篇文章:Python--Redis實戰(zhàn):第三章:Redis命令:第一節(jié):字符串 網(wǎng)站可以從用戶的訪問、交互、...
閱讀 3036·2023-04-25 18:06
閱讀 3294·2021-11-22 09:34
閱讀 2866·2021-08-12 13:30
閱讀 2055·2019-08-30 15:44
閱讀 1668·2019-08-30 13:09
閱讀 1636·2019-08-30 12:45
閱讀 1722·2019-08-29 11:13
閱讀 3616·2019-08-28 17:51