浏览器缓存和压缩优化技术(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(前端工具);

猜你喜欢

转载自blog.csdn.net/studyphp123/article/details/84255490