【PHP面試題】瀏覽器緩存和壓縮優化技術(HTTP緩存機制;Nginx配置緩存策略;前端代碼和資源的壓縮)

一、HTTP緩存機制

1、高併發下只能通過提升服務器負載解決?

不是,可以流量優化,前端優化,服務器優化等等(詳解可參考 PHP如何解決網站大流量與高併發的問題?)。

2、緩存只能做數據庫緩存嗎?

還可以做瀏覽器的緩存,瀏覽器緩存可以降低服務器的壓力,同時也可以節省帶寬和流量。

本節會着重對瀏覽器的緩存進行講解

3、緩存分類

1) HTTP緩存模型中,如果請求成功會有三種情況:

200 from cache:即 本地緩存。直接從本地緩存中獲取響應,最快速,最省流量,因爲根本沒有向服務器發送請求;

304 Not Modified協商緩存,瀏覽器在本地沒有命中的情況下,請求頭中發送一定的校驗數據到服務端,如果服務端數據沒有改變,瀏覽器從本地緩存響應,返回 304(即本地緩存如果失效,此時請求頭會帶參數到服務端,讓服務器端去判斷一下該資源在服務器端是否過期,如果沒有過期,也即緩存還有效,就告訴瀏覽器還可以使用本地緩存,最終返回 304;否則返回真實數據。即先去服務器端驗證一下文件是否被修改過)【特點:快熟速,發送的數據很少,只返回一些基本的響應頭信息,數據量很小,不發送實際響應體】;

200 OK:以上兩種緩存全都失敗,服務器返回完整響應。沒有用到緩存,相對最慢。


2)本地緩存

瀏覽器認爲本地緩存可以使用,不會去請求服務端【它是最快的】。

相關Header:

Pragma:HTTP10時代的遺留產物,該字段被設置爲 no-cache 時,會告知瀏覽器禁用本地緩存,即每次都向服務器發送請求。

Expires:HTTP1.0時代用來啓用本地緩存的字段,expires 值對應一個形如 Thu,31 Dec 2037 23:55:55 GMT 的格式威治時間,告訴瀏覽器緩存實現的時刻,如果還沒到該時刻,標明緩存有效,無需發送請求。

當我們去請求服務器端的時候,服務器端會給我們響應一個 expires 回來,告訴你資源的響應時間,過期時間,我們都知道這個時間是服務器返回的,那它意味着就是我們的服務器時間,我們再去判斷該文件是否過期的時候,是我們的瀏覽器來進行判斷,瀏覽器在進行判斷的時候,使用的是客戶端時間,所以在這兒這兩個時間的基準是不太一樣的,也即在此過程中會有一個致命的缺陷)瀏覽器與服務器的時間無法保持一致,如果時間差距大,就會影響緩存結果(有人會說,我能保證我的服務器時間一定是最正確的時間,但是我們並不能保證客戶端的時間,保證不了使用瀏覽器打開網站的時候它的客戶端時間是否正確),爲了解決該缺陷出現了HTTP1.1。

Cache-Control:HTTP1.1針對 Expires 時間不一致的解決方案,運用 Cache-Control 告知瀏覽器緩存過期的時間間隔而不是時刻,即使具體時間不一致,也不影響緩存的管理。

Cache-Control 給的是一個秒數,即該文件多少秒後過期,不說時間點,這樣就沒有時間對比的問題。


Cache-Control 相關設置:

  • no-store:禁止瀏覽器緩存響應
  • no-cache:不允許直接使用本地緩存,先發起請求和服務器協商
  • max-age=delta-seconds:告知瀏覽器該響應本地緩存有效的最長氣象,以秒爲單位【如:max-age=3600,表示可以緩存3600s(1小時)】

如果三個被同時設定,則優先級如下:

優先級:Pragma > Cache-Control > Expires


3)協商緩存

  • 當瀏覽器沒有命中本地緩存,如本地緩存過期或者響應中聲明不允許直接使用本地緩存,那麼瀏覽器肯定會發起服務端請求。
  • 服務端會驗證數據是否被修改,如果沒有則通知瀏覽器使用本地緩存。
相關Header:

① Last-Modified:通知瀏覽器資源的最後修改時間。(即當我們去頁面上請求一個資源的時候,服務器會給一個響應,響應的時候會響應一個 Last-Modified ,當然我們需要去設置,響應一個 Last-Modified 代表資源的最後修改時間是多少,便於下次請求服務端的時候,我們要帶這個時間過來,服務端會去判斷這個時間節點上之後我們這個文件是否發生了修改,如果沒有修改,則返回 304,告訴我們訪問這個文件的時候,使用本地緩存)

Last-Modified:Last-Modified:Mon, 28 Sep 2015 08:06:43 GMT

在這裏插入圖片描述

If-Modified-Since:得到資源的最後修改時間後,會將這個信息通過 If-Modified-Since 提交到服務器做檢查,如果沒有修改,返回 304 狀態碼。

② ETag:HTTP1.1推出,文件的指紋標識符,如果文件內容修改,指紋會改變。(如果文件進行了修改,Etag就會改變,這裏它脫離了時間的約束,它會更加準確。對於 ETag 來說,它就是文件的一個標識,第一次請求服務器會響應一個 ETag,告訴文件文件資源的標識是多少;下次瀏覽器再去請求服務端的時候,會帶着它去,這時候會換一個名稱,會帶着該值去服務端看,看內容是否發生改變,如果它改變,ETag 的值就會發生改變;如果沒有改變,就會告訴我們瀏覽器使用本地緩存,返回一個 304 的狀態碼)

ETag:“78437822c-6739”

If-None-Match:本地緩存失效,會攜帶此值去請求服務端,服務端判斷該資源是否改變,如果沒有改變,直接使用本地緩存,返回 304。

If-None-Match:“78437822c-6739"


4、緩存策略的選擇

1)適合緩存的內容

不變的圖像,如 Logo,圖標等;
js,css 靜態文件;
可下載的內容,媒體文件

2)建議使用協商緩存

HTML文件;
經常替換的圖片;
經常修改的 js,css 文件;

注意事項
① 經常改變的文件,適合做 協商緩存;
② js,css 文件的加載可以加入文件的簽名來拒絕緩存。

如:不想使用緩存,則可以加入簽名,如下:

// 方法一:
index.css?簽名
// 方法二:
index.簽名.js

3)不建議緩存的內容

用戶隱私等敏感數據(如:購物車信息,用戶個人信息等隱私信息);
經常改變的api數據接口(如果做了緩存,數據返回就不準確了)。

二、Nginx配置緩存策略

1、模擬 Nginx 實現緩存

1)新建一個 demo.php

echo time();

2)打開瀏覽器去訪問 demo.php ,然後打開 F12 查看 Network,可以發現 狀態碼爲 200,說明沒有使用緩存;Response Headers 響應頭 沒有 Last-ModifiedRequest Headers 請求頭 沒有 If-Modified-Since

運行結果:

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

1)修改 demo.php 爲如下代碼:

header('Last-Modified:'. gmdate('D, d M Y H:i:s', time()). ' GMT');

echo time();

2)再次去訪問瀏覽器,打開 Network 中的 Header 來查看,此時 Response Headers 會有 Last-Modified

在這裏插入圖片描述
3)再次刷新,此時發現 Request Headers 中的 If-Modified-Since上一次 Last-Modified 帶過來的時間。

在這裏插入圖片描述

如果想獲取帶過來的時間,需要判斷該文件是否過期。我們來模擬一下,我們可以通過獲取到的請求頭去判斷文件是否過期,但是我們沒有辦法去判斷文件是否被修改過,因爲我們要不停地去做,不停地去改,所以在此處的判斷是不準確的,因此我們來模擬一下 Nginx ,瞭解一下這種意義即可。

1)修改 demo.php 爲如下:

$since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; // 獲取頭

$lifetime = 3600; // 假設該文件 3600s 後過期

// 判斷該文件是否過期
if (strtotime($since) + $lifetime > time()) { // 未過期
    // 通知瀏覽器使用本地緩存
    header('HTTP/1.1 304 Not Modified'); 
    exit;
}

header('Last-Modified:'. gmdate('D, d M Y H:i:s', time()). ' GMT');

echo time();

2)打開瀏覽器去訪問 demo.php 查看一下時間,然後再去刷新瀏覽器,發現時間還是沒有改變,說明沒有過期,因爲我們設置的過期時間爲 3600s,且響應的內容爲 304.

運行結果:

在這裏插入圖片描述

注:對於 Nginx 來說,就是做這樣的判斷,只不過它沒有根據 lifetime ,而是根據文件是否發生了修改來去做的判斷。


2、本地緩存配置

1) add_header 指令:添加狀態碼爲 2XX 和 3XX 的響應頭信息

語法如下:

add_header name value [always]; // 加always,表示在任何的狀態碼下都可以去加這樣的內容

2)可以設置 Pragma / Expires /Cache-Control,這三個頭可以繼承使用

expires 指令:通知瀏覽器過期時長【常用】

語法如下:

expires time; // time 可以是 時,分,秒,天
expires 30s; // 30s 後過期

注:
爲負值時表示 Cache-Control:no-cache;;
當爲正值或者0時,就表示 Cache-Control:max-age = 指定的時間;


如何指定 Expires???

方法一:

在終端下打開 Nginx 配置vim /usr/local/nginx/conf/nginx.conf),找到 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ 這個是我們用來做防盜鏈的時候設置的,其他的不用去管,可以看到最下面有個 expires 30d:瀏覽器告訴服務器返回做30天的緩存操作。

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    #valid_referers none blocked imooc.com *.imooc.com;
    #if ($invalid_referer) 
    #{
    #	#return 403;
    #	rewrite ^/ https://www.baidu.com/img/bd_logo1.png;
	#}
    #accesskey on;
    #accesskey_hashmethod md5;
    #accesskey_arg sign;
    #accesskey_signature "jason$remote_addr";
    expires		30d; // 瀏覽器告訴服務器返回做30天的緩存【圖片的緩存時間】
}

// js 和 css 緩存 12小時
location ~ .*\.(js|css)?$
{
    expires		12h;
}

第一次請求的運行結果:

在這裏插入圖片描述
我們打開 header 頭信息,可以驗證一下圖片的緩存時間是否爲 30天,我們去訪問一下 test.jpg,打開 Network 查看 Last-ModifiedExpires 是否相差 30天?

在這裏插入圖片描述

此時我們可以看到相差確實爲 30天,說明我們在 圖片下設置的 expires 30d; 是有效的。


再去重新請求一下,變成了 304:

在這裏插入圖片描述


方法二:

當爲 max時,會把 Expires 設置爲 "Thu, 31 Dec 2037 23:55:55 GMT"Cache-Control 設置到 10 年。


3、協商緩存相關配置

ETag 指令:指定簽名

ETag on | off;默認是 on;

打開終端去關閉 Etagvim /usr/local/nginx/conf/nginx.conf

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    #valid_referers none blocked imooc.com *.imooc.com;
    #if ($invalid_referer) 
    #{
    #	#return 403;
    #	rewrite ^/ https://www.baidu.com/img/bd_logo1.png;
	#}
    #accesskey on;
    #accesskey_hashmethod md5;
    #accesskey_arg sign;
    #accesskey_signature "jason$remote_addr";
    expires		30d;
    etag		off;
}

注:如果 關閉了 ETagResponse Headers 中還會有 ETag,但是 Request Headers 在判斷的時間,就不會去判斷 ETag 了(即 Request Headers 中沒有 ETag )。

重要的事情說三遍:

在 Nginx 中 expire 用的是非常多的!!!

在 Nginx 中 expire 用的是非常多的!!!

在 Nginx 中 expire 用的是非常多的!!!


當然我們也可以去設置 add_header

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    #valid_referers none blocked imooc.com *.imooc.com;
    #if ($invalid_referer) 
    #{
    #	#return 403;
    #	rewrite ^/ https://www.baidu.com/img/bd_logo1.png;
	#}
    #accesskey on;
    #accesskey_hashmethod md5;
    #accesskey_arg sign;
    #accesskey_signature "jason$remote_addr";
    expires		30d;
    #etag		off;
    add_header 	cache-control max-age=3600 ;
}

我們使用最多的還是 expiresadd_header 我們作爲一個瞭解即可。

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    #valid_referers none blocked imooc.com *.imooc.com;
    #if ($invalid_referer) 
    #{
    #	#return 403;
    #	rewrite ^/ https://www.baidu.com/img/bd_logo1.png;
	#}
    #accesskey on;
    #accesskey_hashmethod md5;
    #accesskey_arg sign;
    #accesskey_signature "jason$remote_addr";
    expires		30d;
    #etag		off;
    #add_header 	cache-control max-age=3600 ;
}

注:以上爲 Nginx 的所有配置,如果面試官問到,我們可以回答:打開 Nginx配置項,開啓 Expires 來做一些相關的配置。


三、前端代碼和資源的壓縮

1、優勢

讓資源文件更小,加快文件在網絡中的傳輸,讓網頁更快的展現,降低帶寬和流量開銷。

2、壓縮方式

1)JS、CSS、圖片、HTML代碼的壓縮(可以將裏面的空白符,變量,進行語法的合併);

JavaScript 代碼壓縮:

JavaScript壓縮的原理一般是:去掉多餘的 空格和回車、替換長變量名、簡化一些代碼寫法等。

$(function(){
    $(".btn").click(function(){
        $.post(...);
    });
});

我們會發現裏面有很多的空白符,雖然代碼的可讀性很好,但是會佔用很多的內存。

var a = 100;
var b = 200;

// 優化後的代碼
var a=100,b=200;

① JavaScript代碼壓縮工具有很多,有在線工具、有應用程序、有編輯器插件;

② 常用壓縮工具:UglifyJS、YUI Compressor、closure Compiler;

打開 tool.css-js.com,我們可以去寫一個測試代碼,如下:

在這裏插入圖片描述

③ UglifyJS:壓縮、語法檢查、美化代碼、代碼縮減、轉化;

④ YUI Compressor:來自 Yahoo、只有壓縮功能(不會轉化語法);

⑤ Closure Compiler:來自 Google、功能和 UglifyJS 類似,壓縮的方式不一樣。


CSS 代碼壓縮:

① 原理跟 JavaScript 壓縮原理類似,同樣是去除空白符、註釋並且優化一些CSS語義規則等;

② 常用壓縮工具:YUI Compressor、CSS Compressor;

③ CSS Compressor:壓縮時可以選擇模式

在這裏插入圖片描述

在這裏插入圖片描述


HTML代碼壓縮:【不推薦使用】

不建議使用代碼壓縮,有時會破壞代碼結構,可以使用 Gzip壓縮,當然也可以使用 htmlcompressor 工具,不過轉換後一定要檢查代碼結構(有可能轉換代碼結構會亂)。

打開 htmlcompressor.com/compressor/ 來測試:

壓縮前:

在這裏插入圖片描述

壓縮後:

在這裏插入圖片描述


圖片壓縮:

① 除了代碼的壓縮外,有時對圖片的壓縮也是很有必要,一般情況下,圖片在 Web 系統的比重都比較大(如:一張圖片本身來說有好幾百K,如 300多K,我們把它壓縮到100K以內,本身來說,會節省流量,而且響應也更快);

② 壓縮工具:tinypng、JpegMini、ImageOptim;

A. https://tinypng.com,可以壓縮 .png.jpg 圖片:

在這裏插入圖片描述

可以把 57KB 的圖片壓縮到 15KB,但是分辨率上並沒有什麼區別。

B. www.jpegmini.com

在這裏插入圖片描述

C. https://imageoptim.com,這個工具我們可以下載下來使用。

在這裏插入圖片描述


2)Gzip壓縮(在服務器開啓 Nginx 中的 Gzip壓縮)。

Nginx配置:
gzip on|off;			   			  # 是否開啓 gzip
gzip_buffers 32 4K| 16 8K  			  # 緩衝(在內存中緩衝幾塊?每塊多大)
gzip_comp_level [1-9]      			  # 推薦6 壓縮級別(級別越高,壓的越小,越浪費CPU計算資源) 
gzip_disable 			   			  # 正則匹配UA 什麼樣的URI不進行 gzip
gzip_min_length 200		   			  # 開始壓縮的最小長度
gzip_http_version 1.0|1.1  			  # 開始壓縮的http協議版本
gzip_proxied               			  # 設置請求者代理服務器,該如何緩存內容
gzip_types text/plain application/xml # 對那些類型的文件用壓縮 如:txt,xml,html,css
gzip_vary on|off					  # 是否傳輸 gzip壓縮標誌

打開 Nginx 終端(vim /usr/local/nginx/conf/nginx.conf),查看一下,我們設置了一些 gzip:

在這裏插入圖片描述

如何查看是否使用了 gzip,我們拿圖片來測試,由於配置中支持 .gif壓縮,因此,我們去請求頭查看一下,Accept-Encoding:gzip, deflate 告訴瀏覽器以 gzip 的形式壓縮圖片,但是 Response Header 響應頭 卻沒有,因爲我們設置的 gzip 只支持 gif,不支持 png;我們可以去修改一下 gzip ,如下所示:

在這裏插入圖片描述

在這裏插入圖片描述

我們發現Response Headers響應頭 也有了 Content-Encoding:gzip,會對該張圖片進行 gzip 壓縮處理。


3)其他工具

自動化構建工具 Grunt(前端工具);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章