浏览器缓存策略:由谁来判断缓存有效
作为前端工程师,开发的应用大部分是基于浏览器的。理解浏览器原理,对设计方案和排查问题是很有帮助的事情。下文介绍了
浏览器的缓存策略
:强缓存和协商缓存,并对如何选择一种合适的缓存方案
进行讨论。
1. 原理
浏览器的缓存策略是通过HTTP协议来控制的,主要涉及Expire和Cache-Control、Last-Modified和ETag等头字段。
1.1. 强缓存
浏览器第一次请求到数据后,缓存到本地。再次请求时,如果浏览器判断缓存未过期,就使用缓存数据;否则,向服务器发起请求新的数据。
通过HTTP/1.0的Expire和HTTP/1.1的Cache-Control字段实现:
-
Expire表示
过期时间
,是一个绝对时间。当服务器和浏览器所在时区不同时,会存在误差; -
Cache-Control的max-age表示
有效时间
,是一个相对时间,解决了Expire存在的误差问题。
Cache-Control还可以实现更小粒度的缓存控制:
-
public:客户端和代理服务器都可以缓存。 -
private:只允许浏览器缓存,代理服务器不能缓存。 -
no-cache:不允许使用强缓存。 -
no-store:不允许使用缓存。 -
s-maxage:类似max-age,指定代理服务器缓存过期时长。
1.2. 协商缓存
服务器在返回数据的同时会
返回一个缓存标识
。浏览器下次请求时,带上缓存标识
。服务器根据缓存标识判断资源是否更新
。如果未更新则返回304
,表示资源未被修改;否则返回200和新的数据
。
缓存标识是服务器用来判断数据是否更新的,可分为两种:
-
更新时间:服务器返回时设置Last-Modified,浏览器请求时设置If-Modified-Since。
-
唯一标识:服务器返回时设置ETag头字段,浏览器请求时设置If-None-Match头字段。
Last-Modified VS ETag :
-
精度:在编辑了文件但最终内容没有改变的情况下,Last-Modified会更新并造成缓存失效;而ETag的值只有在内容发生改变时才会变化。 -
性能:Last-Modified只需要记录一个时间点,而 Etag需要根据文件的内容计算哈希值。 -
优先级:两者同时存在时,服务器会优先选择ETag。
2. 缓存方案
2.1. B站
-
html:no-cache -
JS文件:max-age=31536000(1年) -
CSS文件:max-age=31536000 -
图片:max-age=31536000
2.2. 淘宝
-
html:no-cache -
js文件:max-age=2592000(一个月),s-maxage=86400;max-age=0 -
CSS文件:max-age=2592000,s-maxage=3600 -
图片:max-age=15552000
2.3. 新浪
-
html:max-age=60 -
js文件:max-age=31536000;max-age=14400;max-age=3600 -
CSS文件:public, max-age=14400 -
图片:max-age=31536000(一年)
2.4.小结
强缓存和协商缓存最大的区别在于:前者由浏览器判断资源是否过期,后者由服务器判断资源是否更新。
强缓存在使用缓存时,不需要网络请求
服务器。所以合适js、css、img等文件体积大
且更新频率低
的静态资源。如果想更新未过期的缓存数据
,也可以通过重新命名文件
进行刷新。但是,这种不适合html文件,因为html文件名一般是网站对外公开的url的一部分,不适合随意更改。
协商缓存每次都需要请求服务器,判断缓存是否更新,所以适合更新频率高
且可以随时刷新缓存
的场景,例如网站首页的html文件。
3. 实现方式
默认情况下,浏览器会对静态资源使用强缓存,对html使用协商缓存(请求时 max-age=0)。
3.1. HTML缓存
通过 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属性来设置强缓存。用法简单,不需要服务器支持。适合更新频率低的页面。
3.2. nginx缓存
当nginx作为静态资源服务器时,通过配置保证浏览器缓存的默认行为是一致的。
# 禁止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;
}
# 对于更新相对频繁且比较重要的脚本,可以把强缓存有效时间控制在一天内,强缓存失效后进行协商缓存。
location ~.*consult\.js$
{
expires 86400s;
}
3.3. webpack文件名hash
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值也不一样。