知识分享!我希望你知道这些浏览器缓存知识
浏览器缓存一直是个老生常谈的话题,也是面试官常常用来鉴别面试者的利器,作为前端来讲这块知识是属于必须掌握的,再者利用好缓存也是做性能优化的有效方法。本文将从缓存原因、缓存读写顺序,缓存位置以及缓存策略这几个角度介绍浏览器缓存,并且最后给出实践的应用举例。

为什么要缓存
很多同学知道缓存的位置和字段,知道怎么用,但是你有没有想过为什么我们的页面需要浏览器缓存呢?
缓存可以减少用户等待时间,提升用户体验,直接从内存或磁盘中取缓存数据肯定是比从服务器请求更快的;
减少网络带宽消耗:对于网站运营者和用户,带宽都代表着成本,过多的带宽消耗,都需要支付额外的费用。试想一下如果可以使用缓存,只会产生极小的网络流量,这将有效的降低运营成本。
降低服务器压力:给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,降低服务器的压力。
缓存读写顺序
当浏览器对一个资源(比如一个外链的 a.js
)进行请求的时候会发生什么?请从缓存的角度大概说下:


缓存位置

从浏览器开发者工具的 Network
面板下某个请求的 Size
中可以看到当前请求资源的大小以及来源,从这些来源我们就知道该资源到底是从 memory cache
中读取的呢,还是从 disk cache
中读取的,亦或者是服务器返回的。而这些就是缓存位置:

Service Worker
是一个注册在指定源和路径下的事件驱动 worker
;特点是:
运行在
worker
上下文,因此它不能访问DOM
;独立于主线程之外,不会造成阻塞;
设计完全异步,所以同步
API
(如XHR
和localStorage
)不能在Service Worker
中使用;最后处于安全考虑,必须在
HTTPS
环境下才可以使用;
说了这么多特点,那它和缓存有啥关系?其实它有一个功能就是离线缓存:Service Worker Cache
;区别于浏览器内部的 memory cache
和 disk cache
,它允许我们自己去操控缓存,具体操作过程可以参看 Using_Service_Workers;通过 Service Worker
设置的缓存会出现在浏览器开发者工具 Application
面板下的 Cache Storage
中。

缓存按照缓存位置划分,其实还有一个 HTTP/2
的内容 push cache
,由于目前国内对 HTTP/2
应用还不广泛,且网上对 push cache
的知识还不齐全,所以本篇不打算介绍这块
缓存策略

根据 HTTP header
的字段又可以将缓存分成强缓存和协商缓存。强缓存可以直接从缓存中读取资源返回给浏览器而不需要向服务器发送请求,而协商缓存是当强缓存失效后(过了过期时间),浏览器需要携带缓存标识向服务器发送请求,服务器根据缓存标识决定是否使用缓存的过程。强缓存的字段有:Expires
和 Cache-Control
。协商缓存的字段有:Last-Modified
和 ETag
。
Expires
Expires
是 HTTP/1.0
的字段,表示缓存过期时间,它是一个 GMT
格式的时间字符串。Expires
需要在服务端配置(具体配置也根据服务器而定),浏览器会根据该过期日期与客户端时间对比,如果过期时间还没到,则会去缓存中读取该资源,如果已经到期了,则浏览器判断为该资源已经不新鲜要重新从服务端获取。由于 Expires
是一个绝对的时间,所以会局限于客户端时间的准确性,从而可能会出现浏览器判断缓存失效的问题。如下是一个 Expires
示例,是一个日期/时间:
Expires: Wed, 21 Oct 2020 07:28:00 GMT
Cache-Control
它是 HTTP/1.1
的字段,其中的包含的值很多:
max-age
最大缓存时间,值的单位是秒,在该时间内,浏览器不需要向浏览器请求。这个设置解决了Expires
中由于客户端系统时间不准确而导致缓存失效的问题;must-revalidate
:如果超过了max-age
的时间,浏览器必须向服务器发送请求,验证资源是否还有效;public
响应可以被任何对象(客户端、代理服务器等)缓存;private
响应只能被客户端缓存;no-cache
跳过强缓存,直接进入协商缓存阶段;no-store
不缓存任何内容,设置了这个后资源也不会被缓存到内存和硬盘;
Cache-Control
的值是可以混合使用的,比如:
Cache-Control: private, max-age=0, no-cache
当混合使用的时候它们的优先级如下图所示:

当 Expires
和 Cache-Control
都被设置的时候,浏览器会优先考虑后者。当强缓存失效的时候,则会进入到协商缓存阶段。具体细节是这样:浏览器从本地查找强缓存,发现失效了,然后会拿着缓存标识请求服务器,服务器拿着这个缓存标识和对应的字段进行校验资源是否被修改,如果没有被修改则此时响应状态会是 304,且不会返回请求资源,而是直接从浏览器缓存中读取。
而浏览器缓存标识可以是:Last-Modified
和 ETag
:

缓存应用
静态资源
比如页面引入了一个 JQuery
,对于页面来说这个脚本就是一个工具库,基本上是不会发生变化的,对于这种资源可以将它的缓存时间设置得长一点,比如如下这个地址的脚本:

频繁变化的资源
对于频繁变化的资源,比如某个页面经常需要调整,那么这个页面就需要在每次请求的时候都进行验证,可以在响应头这样设置:
cache-control: no-cache
不进行缓存
当然并不是所有请求都能被缓存,无法被浏览器缓存的请求如下:
HTTP
信息头中包含Cache-Control: no-cache
,pragma: no-cache(HTTP1.0)
,或Cache-Control: max-age=0
等告诉浏览器不用缓存的请求;需要根据
Cookie
、认证信息等决定输入内容的动态请求是不能被缓存的;经过
HTTPS
安全加密的请求;POST
请求无法被缓存;HTTP
响应头中不包含Last-Modified/Etag
,也不包含Cache-Control/Expires
的请求无法被缓存;
了解更多,请点击:https://www.bilibili.com/video/BV15z4y1y7e7/
作者:大海我来了
链接:https://juejin.cn/post/6911482888491892749
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。