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