谈到前端性能优化,就不得不提浏览器缓存,它是很重要也很常见的一种方式。但是很多同学仅有缓存的意识,对缓存并没有一个比较清晰的认识。
浏览器缓存分类
- HTTP 状态码 200 from memory cache
- 说明:直接从本地缓存中获取响应
- 特点:最快速、最省流量,因为根本没有向服务器发送请求
- HTTP 状态码 304 not modified
- 说明:协商缓存,浏览器在本地没有命中的情况下请求头发送一定的校验数据到服务器,如果服务器没有改变该资源,浏览器从本地缓存响应
- 特点:快速,发送的数据很少,只返回一些基本的响应头信息,数据量很小,不发送实际的响应体
- HTTP 状态码 200 ok
- 说明:以上两种都失败,服务器返回完整响应
- 特点:没有用到缓存,相对较慢
强缓存(200 from memory cache)
浏览器认为本地缓存可以使用,就不会去请求服务器。
以下是相关 HTTP 头信息:
Pragma
HTTP 1.0 时代产物,该字段被设置为 no cache 时,会告知浏览器禁用本地缓存,即每次向服务器发送请求。
Expires
HTTP 1.0 时代产物,用来启用本地缓存。expires 的值对应一个形如 1969-12-31T23:59:59.000Z 的格林威治时间,告诉浏览器缓存的过期时间,如果该时间还没有到,表明缓存有效,无需发送请求。
存在问题:
expires 的时间是服务器设置,但是在发送到客户端后,过期时间参考的是客户端时间。如果客户端和服务器的时间不能保持一致,时间差距较大,就会影响缓存结果。
正因为这个原因,在 HTTP 1.1 版本里引入了 Cache-Control。
Cache-Control
HTTP 1.1 针对 Expires 时间不一致给出的解决方案,运用 Cache-Control 告知浏览器缓存时间的间隔,而不是过期时间的时刻,即使两端的时间不一致也不会影响缓存结果。
可选项
- no-store:禁止浏览器使用缓存
- no-cache:不允许直接使用缓存,需要先发起请求和服务器协商,即使用协商缓存
- max-age=delta-seconds:告知浏览器该响应的本地缓存最长有效时间,单位秒
优先级
Pragma > Cache-Control > Expires
协商缓存(304 not modified)
当浏览器没有命中本地缓存,如本地缓存过期或者响应中声明不允许直接使用本地缓存。这时浏览器会向服务器发送请求,服务器会验证数据是否修改,如果没有,则通知浏览器使用本地缓存。
以下是相关 HTTP 头信息:
Last-Modified(响应头)
通知浏览器资源最后修改的时间,格式如 1969-12-31T23:59:59.000Z 的格林威治时间。
If-Modified-Since(与 Last-Modified 对应,请求头)
得到资源的最后修改时间后,会将这个信息通过 If-Modified-Since 提交到服务器做检查,如果没有修改,则返回 304 状态码。格式和 Last-Modified 一样。
ETag(响应头)
HTTP 1.1 版本里引入,文件的指纹标识符,如果内容修改,指纹会改变。
If-None-Match(与 ETag 对应,请求头)
本地缓存失效,会携带此值去请求服务器,服务器判断该资源是否发生改变,如果没有改变直接使用本地缓存,返回 304 状态码。
浏览器缓存实现方法
PHP 实现协商缓存
$lastModifiedTime = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
$expireTime = 3600;
if (strtotime($lastModifiedTime) + $expireTime > time()) {
header('HTTP/1.1 304 Not Modified');
exit;
}
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
echo time();
Nginx 缓存配置
强缓存配置
add_header 指令:添加状态码为 2XX 和 3XX 的响应头信息
add_header name value [always];
可以设置 Pragma/Expires/Cache-Control,可以继承
expires 指令
expires time;
为负值表示 Cache-Control:no-cache;
非负值表示 Cache-Control:max-age=指定时间
协商缓存配置
etag 指令 on|off 默认为 on
如 etag off
缓存使用场景
强缓存
- 很少改变的图片,如 logo、图标等
- 很少改变的 js、css 等静态文件
- 可下载的内容,如 word、pdf 文档等
协商缓存
- html 文件
- 经常替换的图片
- 经常修改的 js、css 等静态文件
js、css 文件可以加入签名来拒绝缓存,如 index.js?_=1523370525369
不建议缓存
- 用户隐私、敏感数据
- 经常改变的 API 数据接口