vlambda博客
学习文章列表

浏览器缓存策略:由谁来判断缓存有效

 作为前端工程师,开发的应用大部分是基于浏览器的。理解浏览器原理,对设计方案和排查问题是很有帮助的事情。下文介绍了浏览器的缓存策略:强缓存和协商缓存,并对如何选择一种合适的缓存方案进行讨论。

1. 原理

 浏览器的缓存策略是通过HTTP协议来控制的,主要涉及Expire和Cache-Control、Last-Modified和ETag等头字段。

image-20220320163117879

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)。

image-20211205161052538
image-20211205161301630

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值也不一样。

参考资料