当我们在访问一个页面时,浏览器会先加载HTML文件,然后边解析边加载关联的静态资源,所有资源加载解析完毕后,最终被渲染到屏幕上。
对浏览器来说,用户每次打开页面,都要去请求服务器获取页面资源。如果页面没有更新,每次都去请求服务器是没有必要的
。所以,需要有一个缓存功能:把下载的资源先存起来,下次访问页面时直接读取本地数据,从而提高了页面访问速度。
对于服务器来说,需要对每个请求进行解析url,读取文件和返回数据。而服务器的处理能力是有上限的,所以如果能通过缓存减少不必要的请求,将会降低服务器的负载,让它可以做更多有意义的事情
。
综上所述,为了提高网页的访问速度,降低服务器的负载
,我们需要一个缓存方案,而应用最广的就是http缓存。
首先,比较容易想到的方法就是:服务器在返回资源时指定过期时间
,浏览器拿到资源后存起来,后续请求如果资源未过期就返回缓存数据,不用再请求服务器了
。
这是http1.0的方案,即通过Expires头字段指定过期时间。
Expires: Thu, 05 May 2022 08:13:07 GMT
这里有一个问题:Expires是一个绝对时间
,所以要求浏览器和服务器两者的系统时间必需保持同步
。
首先,Expires使用的是GMT时间,解决了时区问题。但是,如果浏览器和服务器的系统时间不一致,将会导致浏览器无法准确地判断过期时间。
为了解决这个问题,http1.1使用的是max-age,这是一个相对时间
。
cache-control: max-age=60
max-age=60表示资源有效时间为60s,由浏览器自己计算过期时间
,自然也就不存在系统时间同步问题了。
当expires和max-age同时存在时,会优先使用max-age。
当浏览器缓存数据过期了,而服务器资源不一定有更新
,这时候再去下载一次资源也是没有必要的。此时要和服务器验证资源是否已更新,如果未更新就继续使用缓存,否则再去下载新的资源。这就是http1.1中的协商缓存。
当浏览器缓存数据过期了,要和服务器确认资源是否更新,那么如何判断资源已更新呢?
常见的有以下两种方案:
分别对应http1.1中的协商缓存字段etag/ If-None-Match和last-modified/ If-Modified-Since。
服务器返回资源时会带上字段etag或last-modified。当浏览器缓存过期了,会带上If-None-Match或If-Modified-Since字段请求服务器验证资源是否更新。服务器判断资源有更新,就返回200和新资源;否则返回304
。
有了协商缓存,就能在浏览器缓存过期了,而服务器资源实际未更新的情况下避免重复下载。
当etag和last-modified同时存在时,浏览器会优先选择etag
。相对而言,etag精度更高:etag只在文件内容变化时更新,而last-modified更新时不一定代表文件内容有更新。
过期时间和协商缓存是http缓存的核心内容
。除此之外,cache-control还提供了其它指令,可以满足多种应用场景。
PS:当我们刷新页面或第二次在地址栏按回车键,都会直接请求服务器。为了验证本地缓存未过期的情况,需要打开新的tab访问页面。
Cache-Control:max-age=31536000
应用场景:一般用来缓存更新频率低的资源。
像js/css/img可以设置一年,当文件需要及时更新时,只需要更改html中引用的文件名。(所以html一般需要保持较高的新鲜度)
示例:
第一次请求:返回200,max-age=31536000 和 Last-Modified/ETag。
# request
GET /script.js HTTP/1.1
# response
HTTP/1.1 200 OK
Content-Type: application/javascript
Last-Modified: Fri, 03 Jun 2022 08:24:43 GMT
ETag: "6299c54b-24"
Cache-Control: max-age=31536000
第二次请求:返回200,显示数据来自缓存。
# request
GET /script.js HTTP/1.1
# response
HTTP/1.1 200 OK
Content-Type: application/javascript
Last-Modified: Fri, 03 Jun 2022 08:24:43 GMT
ETag: "6299c54b-24"
Cache-Control: max-age=31536000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8yI9E4D9-1659171954471)(C:\Users\Ys\AppData\Roaming\Typora\typora-user-images\image-20220604110638294.png)]
浏览器在使用缓存之前必需跟原始服务器协商验证。
# 方法一
Cache-Control: no-cache
# 方法二
Cache-Control: max-age=0, must-revalidate
适用场景: 对数据实时性要求高,又能用缓存提高访问速度。
一般的html使用协商缓存保证实时性,对于并发量大的页面也可以设置较短的缓存有效时间。
为什么html页面实时性要求高呢?
如果html没有及时更新,将会导致页面引用的js/css等资源(缓存时间较长)无法及时更新。另外,如果涉及接口变更,前后端必须同步更新的情况,那么访问旧的html将会导致接口报错。
示例:
第一次请求:返回200和Last-Modified/ETag。
# request
GET /index.html HTTP/1.1
Cache-Control: max-age=0
# response
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Fri, 03 Jun 2022 07:32:28 GMT
ETag: "6299b90c-14f"
Cache-Control: no-cache
第二次请求:请求带上Last-Modified/ETag,服务器验证资源未更新,返回304。
# request
GET /index.html HTTP/1.1
If-None-Match: "6299b90c-14f"
If-Modified-Since: Fri, 03 Jun 2022 07:32:28 GMT
# response
HTTP/1.1 304 Not Modified
Last-Modified: Fri, 03 Jun 2022 07:32:28 GMT
ETag: "6299b90c-14f"
Cache-Control: no-cache
禁止浏览器缓存资源(响应)和不使用缓存(请求)。
Cache-Control: no-store
适用场景:包含敏感信息等不希望缓存的场景。
示例:
第一次请求:返回200和no-store。
# request
GET /index.html HTTP/1.1
Cache-Control: max-age=0
# response
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Fri, 03 Jun 2022 08:25:15 GMT
ETag: "6299c56b-178"
Cache-Control: no-store
第二次请求:请求服务器,返回200和新资源。
Cache-Control:public,max-age=600,s-maxage=60
public表示浏览器和代理服务器都可以缓存资源;private只有浏览器可以缓存资源;s-maxage表示代理服务器缓存的有效时间。
http缓存除了存在浏览器(私有缓存),还可以存在代理服务器(公共缓存)。例如常见的CDN,即降低了网络延迟,还能降低服务器的负载
。
对于安全性要求高的资源,建议设置为Cache-Control:private,即禁用公共缓存。因为数据一旦被代理服务器缓存下来,就多了一份被攻击的风险。
B站
微信
html:public, max-age=500
JS文件:max-age=31536000(1年),文件命名带版本号或指纹信息,方便及时更新。
CSS文件:max-age=31536000,文件命名带版本号或指纹信息,方便及时更新。
图片:max-age=31536000,文件命名带版本号或指纹信息,方便及时更新。
XHR请求:no-cache,must-revalidate
淘宝
新浪
通过 meta标签 的http-equiv和content来设置报文头:Cache-Control和Expires。
<meta http-equiv="Expires" content="Mon, 20 Jul 2013 23:00:00 GMT" />
<meta http-equiv="Cache-Control" content="max-age=7200" />
用meta标签的http-equiv属性来设置强缓存。用法简单,不需要服务器支持。适合更新频率低的页面。
当nginx作为静态资源服务器时,通过配置http头保证浏览器缓存行为一致。
# html使用协商缓存
location ~.*\.html$
{
add_header Cache-Control no-cache;
# 作用跟Cache-Control no-cache一致;兼容HTTP/1.0
add_header Pragma no-cache;
}
# 对于更新频率低的,缓存有效时间可设置长一点。
location ~.*\.(js|css|png|jpg)$
{
expires 365d;
}
entry:{
main: path.join(__dirname,'./main.js'),
vendor: ['react', 'antd']
},
output:{
path:path.join(__dirname,'./dist'),
publicPath: '/dist/',
filname: 'bundle.[contenthash].js'
}
通过webpack打包,自动给文件名加上hash值。其中,contenthash表示hash值由文件内容计算得到,内容不同产生的contenthash值也不一样。
为了提高网页的访问速度,降低服务器的负载,我们需要一个缓存方案,而应用最广的就是http缓存。
http缓存的核心是过期时间和协商缓存:服务器在返回资源时指定过期时间,浏览器拿到资源后存起来,后续请求如果资源未过期就返回缓存数据,不用再请求服务器了;当浏览器缓存过期后,再和服务器协商确认资源是否更新,如果更新则返回新资源,否则返回304。
基于http缓存,我们可以实现对静态资源的缓存控制,从而提升网站性能。通常会把js和css等更新频率低的资源设置较长的过期时间,把html等实时性要求高的资源设置为不缓存或者强制协商缓存。
最后,列举了各大网站的缓存方案可供借鉴,介绍了通过html和nginx等方式来实现缓存配置。