动手实践了解浏览器缓存机制

719 阅读6分钟

我正在参加「掘金·启航计划」
本文会让读者在本地搭建一个服务,然后通过实际操作来更直接的了解浏览器缓存机制

观看本文前你需要有一定的docker 和 nginx基础

网上写浏览器缓存的文章比较多,一般都是文字加图片带过了,自己没有亲自实践过其实很容易忘记这部分的知识,本文就是让你来自己亲自感受一下浏览器的缓存功能,所谓看再多理论不如自己亲自尝试嘛

准备工作,首先我们启动一个nginx容器,docker基础大家自行弥补哈,不用docker的同学可以在本机装一个nginx哟,效果也是一样的

docker run -p 80:80 --name some-nginx -v /Users/xiaoming/mjw/blog/cache/html:/usr/share/nginx/html:ro /Users/xiaoming/mjw/blog/cache/nginx.conf:/etc/nginx/nginx.conf  -d nginx

挂载两个目录 html目录则是我们的资源目录,nginx.conf就是我们的配置文件,我们可以直接通过localhost访问html目录下的文件啦(加入html目录下有个1.txt则直接localhost/1.txt就可以啦)

#user  nobody;
worker_processes  1;

#event块
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   /usr/share/nginx/html;
        }
    }
}

一、强缓存

强缓存有两种方式:expires和cache-control

1、expires

expires是http1.0时代的产物,是一个比较过时的字段,但不影响我们对他的演示 修改我们的nginx文件

 server {
        add_header expires 20s;
        listen       80;
        server_name  localhost;
        location / {
            add_header Expires "Mon, 20 Feb 2023 15:30:20 GMT";
            root   /usr/share/nginx/html;
        }

添加一个请求头Expires设置一个过期的绝对时间(GMT时间,所以会和我们这差8小时,一般设置可以设置成过期时间减去八小时),在这时间之前的则是缓存的,之后的则表示缓存过期,我这边呢设置好后重启容器

docker restart some-nginx

然后打开谷歌浏览器一看

image.png 无论我怎么访问都是304,这是怎么回事呢,这可跟我的理论有出入了,一般304都是走的协商缓存呀咋回事呢,经过我一番研究,发现我们直接访问资源的请求头带了一个字段

image.png 这个字段一带,就表示直接走协商缓存,所以又请求到服务器去了。这我就疑问了,一般请求头都是浏览器自己的行为我们用户很难去控制。

那意思是我这边的Expires字段就没法验证了???其实验证不了也问题不大,毕竟是一个过时的字段,所以很多主流浏览器可能对他的支持并不是很友好,但是呢经过的我的一番研究,发现火狐浏览器是可以验证这个字段的,也就是火狐浏览器他的请求头不会自带cache-control字段(不得不说火狐浏览器yyds),可以看下面两张图(最后谷歌可能主动访问资源就会带这个字段,如果不想带可以被动去访问,下面cache-control的时候也用的img标签被动请求亲测有效)

image.png

image.png 那为什么这个字段被逐渐淘汰了呢?其实主要还是他依赖的是我们客户端的时间,客户端时间是可以随意修改的,这就导致客户端时间和服务端时间如果有很大出入那么这个字段的意义就没多大了

2、cache-control

cache-control是http1.中为了规避expires字段中提到的问题

当cache-control和expires字段同时存在时,cache-control优先级更高.

cache-control可以有以下几个字段可以设置

字段含义
public表示客户端和服务端都可以缓存
private表示只有客户端可以进行缓存
no-store不使用缓存
no-cache有缓存,但是否使用缓存需要协商缓存来判断
max-age缓存有效期,单位s,是个相对时间,一般含义就是资源请求后的某个时间段后过期

下面我们进行实操,这次为了规避上面chrome那个坑货每次都会带上cache-control:max-age=0这个请求头的问题,这里我们对访问的文件进行修改,我们在html目录下新增index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <img src="/1.png" alt="">
</body>
</html>

可以看到浏览器会被动请求一张图片,主动访问资源被坑,那我们就被动让他请求,这样就不会带那个请求头啦. 修改nginx.conf配置

server {
        add_header expires 20s;
        listen       80;
        server_name  localhost;
        location / {
            add_header cache-control "max-age=10";
            root   /usr/share/nginx/html;
        }

新增请求头,设置max-age为10表示在请求后的10s内是缓存中,10s后缓存过期,重启容器后访问浏览器第一次可以看到是访问的服务器资源

image.png 当我们刷新后就会发现他已经读取缓存中了

image.png

memory cache表示目前缓存在内存中,可以直接从内存中读取,因为是保存在内存中所以当我们当前的tab页关闭后对应的内存也会被清除,这时候在打开一个新tab资源就会从磁盘中进行加载也就是disk cache

如下图就表示存在磁盘中

image.png

修改我们的ngxin.conf将请求头修改为no-cache就会发现资源走了协商缓存的逻辑

image.png 还有几个字段大家伙可以自己试试,这边我就不一一展示了,当然cache-control是可以设置多字段的哈。 你可以设置为 cache-control: private,max-age=100 类似这样

二、协商缓存

1、last-modified/If-Modified-Since

原理很简单,第一次请求的服务端会返回一个last-modified值,表示资源的最后修改时间,下次请求的时候这个值会被客户端当作If-Modified-Since的值放在请求头中,服务器拿到后进行比较,如果最后修改时间变了则不走缓存,否则仍旧走缓存

修改nginx配置,我们先需要把后面要讲到的也就是更高优先级的协商缓存给禁用掉,也就是etag off;

 server {
        #server全局块
        listen       80;
        server_name  localhost;
        #location块
        etag off; 
        location / {
            add_header cache-control "no-cache";
            root   /usr/share/nginx/html;
        }
    }

image.png 可以看到当我们再次访问的时候走了304缓存,因为请求头和响应头的值是相同的,当我们修改资源时就会发现值不同了他又获取服务器资源了

image.png 可以看到当我们修改后请求头携带的时间还是上一张图片的时间,但是响应头的已经被做修改了,所以会是200状态码

etag/If-None-Match

原理和上面类似,只不过不是用最后修改时间来做标识,因为可能内容不变但是时间会不同,etag的作用类似计算文件的一个唯一标识,用这个唯一标识来代替最后修改时间,当etag和上面的last-modified同时存在时,etag优先级更高!

我们只需把etag off 去掉即可

server {
        #server全局块
        listen       80;
        server_name  localhost;
        location / {
            add_header cache-control "no-cache";
            root   /usr/share/nginx/html;
        }
    }

到此为止,我们的实践就结束了,其实很简单,就是不停的去修改nginx配置文件来添加请求头,达到我们想要的效果,用docker的同学别忘了重启容器哟