这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
前言
网络层面的性能优化,使用缓存是一个最直接有效的方案。使用缓存可以减少网络 IO 消耗,提高访问速度,效果显著。因此,认识一下浏览器的缓存机制很有必要。
上图 Network 面板的截图中,我们可以看到 size 列可以看到缓存的获取来源。
浏览器缓存机制有四个方面,它们按请求的优先级依次排列如下:
- Memory Cache
- Service Worker Cache
- HTTP Cache
- Push Cache
Chrome 官方的缓存决策流程图
其中,HTTP 缓存是最主要、最具代表性的缓存策略,我们先来认识一下这位 HTTP 缓存机制。
HTTP 缓存机制
HTTP缓存又分为强缓存和协商缓存,强缓存优先级高于协商缓存,资源没有命中强缓存,才会走协商缓存。
强缓存
特征
- 强缓存由 HTTP 头中的 Expires 和 Cache-Control 两个字段控制
- 如果浏览器判断目标资源命中强缓存,则直接从缓存中获取资源
- 强缓存命中时,返回HTTP状态码为 200
从 expires 到 Cache-Control
- expires 是一个时间戳,如果本地时间小于 expires 设定的过期时间,那么就直接去缓存中取这个资源。
- 问题:时间戳由服务端生成,客户端和服务端之间可能存在时间差
- 解决:可以在
Cache-Control中配置max-age,max-age设定的是相对时间长度,表示在时间长度内有效。从而规避 expires 的时差问题。 Cache-Control的max-age优先级高于expiresmax-age可以视作是对expires的补位/替换,但如果有向下兼容的诉求,expires还是要的。
Cache-Control 的其他配置项
- s-maxage:仅在代理服务器中生效
- public 与 private:
- public:既可以被浏览器缓存,也可以被代理服务器缓存;
- private:只能被浏览器缓存(默认)
- no-store:绕开浏览器,直接向服务端去确认该资源是否过期(走协商缓存)
- no-cache:不使用任何缓存策略,直接向服务端请求资源
协商缓存
特征
- 协商缓存是浏览器和服务端合作的缓存策略
- 浏览器询问服务端是否使用缓存,如果服务端响应 304(Not Modify),即资源未改动,则使用浏览器缓存。
Last-Modified
实现
- 启用协商缓存,首次请求时,响应头携带
Last-Modified(时间戳)返回 - 之后每次请求,带上
If-Modified-Since,值为首次请求时的Last-Modified的值 - 服务器对比
If-Modified-Since和资源的最后修改时间是否一致- 如果一致,返回 304
- 如果不一致,返回完整的响应内容,并在响应头添加新的
Last-Modified
问题
Last-Modified 存在一些弊端,
- 文件编辑过,内容未修改,但最后修改时间改变了
- 文件内容修改了,但修改速度过快(未满1s),而 If-Modified-Since 只能检查到以秒为最小计量单位的时间差 对于上面两种场景,Last-Modified 无法正确判断资源是否变化。
Etag
为了解决上述问题,Etag 作为 Last-Modified 的补充出现了。
- 首次请求时,响应头携带
ETag返回 - 之后每次请求,携带
if-None-Match(值为ETag的值) - 当 Etag 和 Last-Modified 同时存在时,以 Etag 为准
弊端: Etag 生成过程需要服务器额外付出开销,影响服务端性能。