缓存可以说是性能优化中简单高效的一种优化方式了,他可以显著减少网络传输带来的消耗。
对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。
什么是缓存
当我们第一次访问网站的时候,比如juejin.cn,浏览器会把网站上的图片和数据下载到本地,当我们再次访问的时候,网站资源就会从本地加载出来,这就是缓存。
缓存的好处
- 缓解服务器压力,不用每次都去请求某些资源
- 提升性能,打开本地资源肯定会比请求服务来的快
- 减少带宽消耗,当我们使用缓存时,只会产生很小的网络消耗。 web缓存种类:数据库缓存、CDN缓存,代理服务器缓存,浏览器缓存
缓存位置
从缓存位置上来说分为四种,并且各有优先级,当一次查找缓存且都没有命中的时候,才会去请求网络
- Service Wroker
- Memory Cache
- Disk Cache
- Push Cache
- 网络请求
Service Worker
Service Worker的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
当Service Worker没有命中缓存的时候,就会调用fetch函数获取数据。也就是说,没有在Service Worker 命中缓存的话,会根据缓存查找优先级取查找数据。但是是从Memory Cache中还是从网络请求中获取的数据,浏览器就显示我们是从Service Worker中获取的内容。
Memory Cache
Memory Cache也就是内存中的缓存,读取内存中的数据当然比磁盘快。但是内存缓存虽然读取快,但缓存的持续性很短,会随着进程的释放而释放。一旦我们关闭Tab页面,内存中的缓存也就被释放了。
当我们访问过页面后,再次刷新页面,可以发现很多数据都来自内存缓存。
既然内存缓存这么快,为什么不能数据都存在放在内存中呢?这是因为 首先计算机的内存一般比硬盘容量小的多,操作系统需要精打细算内存的使用,所以能使用的内存必然不多。
Disk Cache
Disk Cache也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储在磁盘中,比之Memory Cache胜在容量和存储时效性上。
在所有浏览器缓存中,Disk Cache覆盖面基本是最大的。他会根据HTTP Header中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且及时存在跨站点的情况下,相同地址的资源一旦被硬盘存下来,就不会再次去请求数据。
Push Cache
Push Cache 是HTTP/2中的内容,当以上三种缓存都没有命中时,它才会被使用,并且缓存时间也很短暂,只会在会话(Session)中存在,一旦会话结束就被释放。
网络请求
如果所有缓存都没有命中的话,就只能发起请求来获取资源了。
缓存策略
通常浏览器缓存策略分为两种:强缓存和协商缓存。并且缓存策略都是通过设置HTTP Header来实现的。
强缓存
强缓存可以通过两种HTTP Header实现:Expires
和Cache-Control
。强缓存表示缓存期间不需要请求,state-code
为200。
Expires
expires:Sat, 17 Dec 2022 03:30:46 GMT
Expires是HTTP/1的产物,表示资源会在Sat, 17 Dec 2022 03:30:46 GMT
后过期,需要再次请求。并且Expires
受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
Cache-control
cache-control:max-age=31536000
cache-control
出现在HTTP/1.1,优先级高于Expires
。该属性值表示资源会在31536000s后过期,需要再次请求。
常见指令的作用
指令 | 作用 |
---|---|
public | 表示相应可以被客户端和代理服务器缓存 |
private | 表示响应值可以被客户端缓存 |
max-age=30 | 缓存30s后就过期,需要重新请求 |
s-maxage=30 | 覆盖max-age,作用一样,只在代理服务器中生效 |
no-store | 不缓存任何请求 |
no-cache | 资源被缓存,但是立即失效,下次发起请求验证资源是否过期 |
max-stale=30 | 30s内,即使缓存过期,也继续使用缓存 |
min-fresh=30 | 希望在30s内获取最新的响应 |
协商缓存
向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源。
协商缓存可以通过设置HTTP Header实现:Last-Modified
和Etag
。
当浏览器发起请求验证资源时,如果资源没有做任何改变,那么服务器就会返回304状态码,并且更新浏览器缓存有效期。
Last-Modified 和 if-Modified-Since
Last-Modified
表示本地文件最后修改日期,If-Modified-Since
会将Last-Modified
的值发送给服务器,询问服务器该日期后资源是否有更新,若有更新就会将新的资源发送回来,否则返回304状态码。
Last-Modified
存在的弊端:
- 如果服务端打开缓存文件,即使没有对文件进行修改,还是会造成
Last Modified
被修改,服务端不能命中缓存然后发送相同资源。 - 因为
Last-Modified
只能以秒计算,如果在不可感知的时间内修改完成文件,那么服务器会认为资源还是命中了,不会返回正确的资源。
因为上面这两个弊端,所以在HTTP/1.1出现Etag
。
Etag 和 if-None-Match
Etag
类似文件指纹,If-None-Match
会将当前Etag
发送给服务器,询问改资源Etag
是否变动,有变动就将新资源发送回来。Etag
比Last-Modified
优先级高。
如果什么缓存策略都没设置,浏览器会采用启发式的运算,同在会去取响应头中的Date
减去 Last-Modified
值的10%作为缓存时间。
实际场景应用缓存策略
频繁变动的资源
对于频繁变动的资源,首先需要使用Cache-Control: no-cache
使浏览器每次都请求服务器,然后配合Etag
和Last-Modified
来验证资源是否有效,这样做法虽然不能节省请求数量,但是显著减少响应数据大小。
代码文件
这里指HTML外的代码文件,因为html文件一般不缓存或缓存时间很短。
一般来说,现在会使用打包工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期Cache-Conttrol: max-age=3153600
,这样只有html文件引入的文件名发生变更才会去下载最新的代码文件,否则就一直使用缓存。
刷新操作方式,对缓存的影响
描述 | 操作 | 强缓存 | 协商缓存 |
---|---|---|---|
正常操作 | 地址栏输入url,跳转链接,前进后退 | 有效 | 有效 |
手动刷新 | f5,点击刷新按钮,右击菜单刷新 | 失效 | 有效 |
强制刷新 | ctrl + f5 | 失效 | 失效 |