在我们开发网站的时候往往对网站的性能有极高的需求,当然,外包的除外哈.我见过的外包对效率要求特别高.对代码质量以及性能要求都不是很高.所以这里就不说明太多.高性能就意味着要使用各式各样的缓存,后台redis/memcached缓存等,前台就是浏览器缓存.什么是浏览器缓存.简单来说,就是浏览器继续使用从服务器上的内容,而不去使用服务器上的内容.那如何使用呢,在使用之前,我们先来讲讲浏览器刷新机制.
浏览器刷新
常见的浏览器刷新有如下几个,Ctrl+F5,Ctrl+R,F5,还有个就是转到,前进.不要认为前进就不是刷新了.其实这个是错误的.下面我们来讲讲这几个区别.
Ctrl + F5:
这个在开发过程中,经常使用,为什么说呢,Ctrl+F5为强制刷新,让浏览器不遵守缓存协议,强制的全部重新在服务器上去请求.所有资源都全部重新的去请求.
Ctrl + R|F5:
让浏览器遵守缓存协议,例如last-modified,etag等这些.如果这是服务端返回的是304就认为没有修改,就会直接调用之前的内容.具体的等下面在讲解.用户最多的操作
前进->:
还有类似于这种操作,就是输入浏览器直接回车.浏览器会将代码Expires的并且没有过期的全部使用,以最少的请求去请求浏览器.就不用担心这种也是强制刷新了.
下面我们就来讲讲一些常见的用了浏览器缓存的例子.
Last-modified
在我们浏览网站的时候,经常能看到这个头部信息,这个头部信息是用来记录最后一次的修改时间.如果网站响应的头部信息有这个,那么下次访问的时候,浏览器会带上一个这样的标识
If-Modified-Since:时间
然后服务端可以根据这个来判断是不是应该可以直接使用缓存.当然,一般动态的网页是很少用这种的,因为不存在传统意义上的最后的修改时间.一般会用于静态网站长时间不修改文件的内容等.我们来做一个简单示例.
<?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-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过来,在来看看
这里我们的程序代码是直接退出程序,没有任何输出,那为什么还有这个输出,这个正式因为浏览器读取自己的缓存.让我们看看响应头.
--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,让我们来判断有效期,这个就是让我们来判断是否使用浏览器缓存的一个标识.所以这个就让我们的浏览器强制使用了缓存.
Etag
这个头又是用来干嘛的,这个又是跟浏览器协商的另一种方法,它于前面的last-modified非常相似,但是它没有用文件的最后修改时间,而是用一段编码俩标记内容,这个编码并没有强制要求,但是我们通常用一个文件内容的md5来作为Etag.一个原则就是,如果一个内容的Etag没有变化,那么这个内容也一定没有更新.让我们来弄一个简单的示例.
<?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是一样的.对于我们来说,也是不合理的,只是用来当成示例而已方便理解.让我们来看看执行效果.
第一次都是强制请求,如果前面的没有处理,就先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过后的结果
我们看见响应头中多了一个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 // 关闭后也有效
我们可以根据不同的场景应用不同的缓存机制了.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。