瀏覽器緩存機制

在咱們開發網站的時候每每對網站的性能有極高的需求,固然,外包的除外哈.我見過的外包對效率要求特別高.對代碼質量以及性能要求都不是很高.因此這裏就不說明太多.高性能就意味着要使用各式各樣的緩存,後臺redis/memcached緩存等,前臺就是瀏覽器緩存.什麼是瀏覽器緩存.簡單來講,就是瀏覽器繼續使用從服務器上的內容,而不去使用服務器上的內容.那如何使用呢,在使用以前,咱們先來說講瀏覽器刷新機制.php

瀏覽器刷新

常見的瀏覽器刷新有以下幾個,Ctrl+F5,Ctrl+R,F5,還有個就是轉到,前進.不要認爲前進就不是刷新了.其實這個是錯誤的.下面咱們來說講這幾個區別.html

Ctrl + F5:
    這個在開發過程當中,常用,爲何說呢,Ctrl+F5爲強制刷新,讓瀏覽器不遵照緩存協議,強制的所有從新在服務器上去請求.全部資源都所有從新的去請求.
Ctrl + R|F5:
    讓瀏覽器遵照緩存協議,例如last-modified,etag等這些.若是這是服務端返回的是304就認爲沒有修改,就會直接調用以前的內容.具體的等下面在講解.用戶最多的操做
前進->:
    還有相似於這種操做,就是輸入瀏覽器直接回車.瀏覽器會將代碼Expires的而且沒有過時的所有使用,以最少的請求去請求瀏覽器.就不用擔憂這種也是強制刷新了.

下面咱們就來說講一些常見的用了瀏覽器緩存的例子.nginx

Last-modified

在咱們瀏覽網站的時候,常常能看到這個頭部信息,這個頭部信息是用來記錄最後一次的修改時間.若是網站響應的頭部信息有這個,那麼下次訪問的時候,瀏覽器會帶上一個這樣的標識web

If-Modified-Since:時間

而後服務端能夠根據這個來判斷是否是應該能夠直接使用緩存.固然,通常動態的網頁是不多用這種的,由於不存在傳統意義上的最後的修改時間.通常會用於靜態網站長時間不修改文件的內容等.咱們來作一個簡單示例.redis

<?php
// 判斷是否含有這個最後一次修改時間,若是有,就強制使用304,讓瀏覽器使用緩存.並終止程序.
if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
    header('HTTP/1.1 304 Not Modified');
    exit;
}
// 添加最後一次的修改時間
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
echo date('Y-m-d H:i:s');

這裏的作法雖然並不合理,沒有判斷時間,只要存在就使用緩存.這僅僅是爲了看下效果,開發程序時應該有更好的作法.讓咱們來看看效果.咱們把這個命令成index.php,放在localhost執行的地址,讓咱們來看看
last-modifyed
第一次訪問,瀏覽器獲得的last-modified這個響應頭,咱們來看看請求頭和響應頭瀏覽器

--Response Headers--
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Fri, 11 May 2018 02:19:51 GMT
Last-Modified:Fri, 11 May 2018 01:37:15 GMT
Server:nginx/1.9.15 (Ubuntu)
Transfer-Encoding:chunked
--Request Headers--
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.9
Cache-Control:no-cache
Connection:keep-alive
Host:localhost
Pragma:no-cache
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

從上面能夠看出,對於第一次訪問的網站,瀏覽器並無緩存.而後當咱們再次使用了F5過來,在來看看
last-modified-2
這裏咱們的程序代碼是直接退出程序,沒有任何輸出,那爲何還有這個輸出,這個正式由於瀏覽器讀取本身的緩存.讓咱們看看響應頭.緩存

--Response Headers--
Connection:keep-alive
Date:Fri, 11 May 2018 02:26:57 GMT
Server:nginx/1.9.15 (Ubuntu)

這裏僅有簡單的幾行,指明瞭web服務器和系統,再來看看請求頭服務器

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.9
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost
If-Modified-Since:Fri, 11 May 2018 01:37:15 GMT
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

添加了一個If-Modified-Since,讓咱們來判斷有效期,這個就是讓咱們來判斷是否使用瀏覽器緩存的一個標識.因此這個就讓咱們的瀏覽器強制使用了緩存.app

Etag

這個頭又是用來幹嗎的,這個又是跟瀏覽器協商的另外一種方法,它於前面的last-modified很是類似,可是它沒有用文件的最後修改時間,而是用一段編碼倆標記內容,這個編碼並無強制要求,可是咱們一般用一個文件內容的md5來做爲Etag.一個原則就是,若是一個內容的Etag沒有變化,那麼這個內容也必定沒有更新.讓咱們來弄一個簡單的示例.memcached

<?php
$date = date('Y-m-d');    // 咱們這裏用當前的日期來代替文件md5
// 判斷請求頭中是否存在if-none-match,若是存在就去判斷md5,若是值同樣,就返回304
if (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)
    && $_SERVER['HTTP_IF_NONE_MATCH'] == 'W/"' . md5($date) . '"') {
    header('HTTP/1.1 304 Not Modified');
    exit;    // 退出
}
// 添加請求頭.
header('Etag: "' . md5($date) . '"');
echo date('Y-m-d H:i:s');

這裏咱們的程序代碼是跟咱們用last-modified是同樣的.對於咱們來講,也是不合理的,只是用來當成示例而已方便理解.讓咱們來看看執行效果.
etag-1
第一次都是強制請求,若是前面的沒有處理,就先Ctrl+F5,這裏就先說明這個.具體的部署仍是跟上一步的步驟同樣.讓咱們看看響應頭

--Response Headers--
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Fri, 11 May 2018 02:45:07 GMT
Etag:W/"64ddf4c4fbf56a689c963872a8325370"
Server:nginx/1.9.15 (Ubuntu)
Transfer-Encoding:chunked

在響應頭裏面增長一項,Etag,固然這個是咱們的程序輸出的.用程序控制的.咱們來看看F5事後的結果
etag-2
咱們看見響應頭中多了一個If-None-Match這個跟咱們上一個請求的Etag值是同樣的.

--Request Headers--
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.9
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost
If-None-Match:W/"64ddf4c4fbf56a689c963872a8325370"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

這個對於咱們來講,在程序中咱們也是直接中止了,而且報出304的狀態碼,可是瀏覽器仍是做出反映了,將之前的值拿過來用了.這個就是咱們的Etag

Etag和Last-Modified

既然這兩個都是用來做用瀏覽器緩存的.那誰的優先級高呢,其實沒有優化級,根據咱們服務的處理來的.咱們若是優先處理Etag那就Etag值高,優先處理Last-Modified就Last-Modified高.

另外一種不一樣的Expires

上面兩種方式已經說明了,可是咱們仍是會常常看到,在咱們退出瀏覽器事後,再次訪問這個網站的時候,仍是讀取的緩存.顯然,咱們上面兩種都不行的.不信你試試.這裏咱們就介紹另一種,Expires,咱們先來例子

<?php
$stream = file_get_contents('./etag-1.png');
$seconds_to_cache = 3600;
$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
header('Content-Type: image/png');
header("Expires: $ts");
echo $stream;

當咱們第一次訪問的時候,用localhost訪問時,會獲得一張圖片.而且緩存1個小時,而後咱們退出瀏覽器.並修改代碼

<?php
exit('111111');
$stream = file_get_contents('./etag-1.png');
$seconds_to_cache = 3600;
$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
header('Content-Type: image/png');
header("Expires: $ts");
echo $stream;

而後在訪問時,依然會獲得咱們的圖片,並無獲得咱們所謂的6個1.這個圖片已經被瀏覽器緩存下來了.Expires是絕對時間.同理還有一個相對的,Cache-Control這裏我就不演示,能夠本身根據代碼來做出演示便可.

最後

咱們根據以上的得出以下結果

Last-Modified -> If-Modified-Since    // 瀏覽器緩存於當前會話
Etags -> If-Modified-Since // 瀏覽器緩存當前會話
Expires    // 關閉後也有效
Cache-Control    // 關閉後也有效

咱們能夠根據不一樣的場景應用不一樣的緩存機制了.

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息