cache

这些缓存主要针对css, js,font等资源,而不是后端接口

Pragma

可选值:no-cache 与 Cache-Control: no-cache 一致

Pragma 是HTTP/1.0标准中定义的一个header属性,通常用来向后兼容基于HTTP/1.0

在 chrome 的 network 右键资源或接口勾选 protocol,可以发现几乎都是 http1.1 或 h2,所以该属性不必太过了解

Expires

Expires 返回一个绝对时间,如果请求时间在Expires指定的时间之前,就能命中缓存,过期之后会重新请求资源

缺点:

  • 客户端修改本地时间和服务器时间不一致时,则会受影响

  • 在Cache-Control响应头设置了 “max-age” 或者 “s-max-age” 指令,Expires 会被忽略

Cache-Control

请求头:Cache-Control

1
2
3
4
Cache-Control: max-age=<seconds>     // 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
Cache-Control: max-stale[=<seconds>] // 客户端愿意接收一个在指定过期时间内的资源(单位秒)
Cache-control: no-cache // 可以在本地,代理服务器缓存,但需要进行验证(协商缓存验证)
Cache-control: no-store // 不使用任何缓存

响应头:Cache-Control

1
2
3
Cache-control: no-cache
Cache-control: no-store
Cache-Control: max-age=<seconds>

处理上面列出来的,其实还有很多,但是这几个比较常见,作为重点介绍

强缓存 和 协商缓存

强缓存:无论http1.0还是1.1的方案,都是在本地缓存中存放一段时间。过期后就需要去服务端重新请求一遍

协商缓存:当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串

测试强缓存

基于 koa + koa-static

  • 在服务端指定 Cache-Control: max-age=10000 缓存 10 秒,Cache-Control本身接受单位是秒,是 koa-static 插件的单位是毫秒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa =require('koa');
const static = require('koa-static');
const path = require('path');
const app = new Koa();
const staticPath = './static'
app.use(
static(
path.join( __dirname, staticPath),
{
maxage: 10000
}
)
)
// 错误处理
app.on('error', err => {
console.log('服务错误', err)
});
app.listen(3388, '0.0.0.0', () => {
console.log('启动成功!端口:3388');
});

nginx

  • 上面如果不好理解,有疑问,那我们用nginx,毕竟我们的项目绝大多数都是跑在nginx服务下的,nginx 此时字段的单位就正常的是秒了

  • 配置目录/usr/local/etc/nginx/nginx.conf 的 nginx.conf,mac 的 nginx.conf 文件在 /usr/local/etc/nginx/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
worker_processes  1;
events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

server{
listen 7675;
server_name localhost;

location / {
root /Users/liuyongshun/Documents/myself/git/all-demo/cache/static/;
index index.html index.htm;
// 设置缓存请求头
add_header Cache-Control max-age=2000;
}
}
}

其他:

  • 如果是放在 oss 上的资源,同样可以设置http头,来制定缓存策略

  • 如果 chrome 的 network 里勾选 disable-cache 缓存则不生效

  • 如果 shift + command + r 或者 ctrl + f5 进行强制刷新,则不会走缓存,而且此时我们可以观察到,请求头自动带上Cache-Control: no-cache

测试协商缓存

http1.0: Last-Modified

  • 第一次请求时,服务器把资源最后修改的时间作为值写入 Last-Modified 作为响应头的缓存标识返回给浏览器

  • 第二次请求时,浏览器自动带上 If-Modified-Since 请求头,服务器将 If-Modified-Since 的时间与资源修改的时间对比,时间不一致,意味着更新,同时更新Last-Modified,时间一致,意味着没更新,服务器返回304状态码,浏览器将从缓存中读取资源

  • 有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,当这种情况出现的时候,就会影响协商缓存的可靠性,所以在 HTTP / 1.1 出现了 ETag

在上面的 koa 代码增加如下两端

1
2
3
4
5
const conditional = require('koa-conditional-get');
.
.
.
app.use(conditional())

nginx 直接上面的配置即可使用 Last-Modified 属性,并返回304

http1.1 Etag

  • 浏览器请求服务器资源,用Etag来设置响应头缓存标识。Etag是由服务端生成的,然后浏览器会将Etag与资源缓存

  • 浏览器会将 Etag 放入 If-None-Match 请求头,服务器会对比两端的标识,不一致,意味着更新,同时更新Etag,两者一致,意味着没更新,服务器会返回304状态码,浏览器将从缓存中读取资源

第一次设置 response header

1
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

第二次设置 request header

1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

如果以上两种方式同时使用,Etag( Http 1.1 ) > Last-Modified ( Http 1.0 )

测试代码

在最初的 koa 上增加如下代码

1
2
3
4
5
const etag = require('koa-etag');
.
.
.
app.use(etag())

nginx 配置

在 http 的下面增加 etag on; 即可

1
2
3
4
5
6
7
http {

etag on;
.
.
.
}

什么时候使用 etag

  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间)

  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since 能检查到的粒度是s级的,这种修改无法判断

  • 某些服务器不能精确的得到文件的最后修改时间

提示:

大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】

用户行为对缓存影响

  • 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求

  • F5:TAB 没有关闭,因此 memory cache 可用,会被优先使用(如果匹配的话)。其次才是 disk cache

  • Ctrl + F5:浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容

Service Worker

Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能

使用注意:

  • 因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全

  • Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的

实现缓存步骤:

  • 首先需要先注册 Service Worker

  • 监听到 install 事件以后就可以缓存需要的文件

  • 在下次用户访问的时候通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据

  • 当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。如果没有在 Service Worker 命中缓存,会根据缓存查找优先级去查找数据。但不管是从 Memory Cache 还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容

返回
顶部