likes
comments
collection
share

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

作者站长头像
站长
· 阅读数 16

一、前置知识

1.1 强缓存

  • 客户端第一次访问服务端资源时,服务端会返回请求的对应资源,告诉客户端将这个资源保存在本地,并且告诉客户端如果在未来的某个时间点之前还访问这个资源,则直接从本地获取就好,不需要再向服务端请求。
  • 服务端在返回对应资源时,同时也返回了Expires/Cache-Control两个字段
    • Expires
      • 为一个绝对时间的GMT格式的时间字符串,代表缓存资源的过期时间,在这个时间点之前访问,则命中缓存
      • 缺点是该字段是根据客户端本地的时间进行对比的,若本地的时间与服务端的时间不一致,则会造成资源混乱
      • 具体的请求过程为:
        • 浏览器第一次发起请求后,response响应头会携带Expires,并将资源和response header保存在本地
        • 当浏览器再次发起请求获取这个资源时,浏览器会先从缓存中找到这个资源,并对比Expires时间和本地当前请求的时间比较,若还没有超过Expires时间,则说明缓存未过期,即命中缓存;反之则重新发起请求获取资源,并重复以上操作
    • Cache-Control有很多属性
      • private:客户端可以缓存
      • public:客户端和代理服务器都可以缓存
      • max-age=t:缓存将在t秒后失效(与Expires的时间格式不一样,这里主要为秒,表示第一次获取该资源后的t秒内该资源都被认为命中缓存)
      • no-cache:需要使用协商缓存来验证缓存数据
      • no-store:所有内容都不会缓存
      • 其描述的是相对时间,采用本地时间计算资源的有效期,即本地时间+max-age,在这段时间内即命中缓存,所以比Expires更可靠
      • 并切Cache-Control比Expires优先级更高

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

1.2 协商缓存

  • 客户端第一次访问服务端资源时,服务端会返回请求的对应资源和资源的一些信息(文件摘要/最后的修改时间),告诉客户端将这个资源保存在本地,当客户端再次请求这个资源时,会将文件摘要/最后的修改时间一并发给服务端,由服务端判断客户端资源是否需要更新,若不需要更新,则直接告诉客户端从本地读取资源(返回状态码304),若需要更新,则将最新的资源(包括最新的文件摘要/最后的修改时间)返回给客户端
    • 最后的修改时间(Last-Modified)
      • Last-Modified:资源的最后修改时间
      • if-Modified-Since:通过比较两个时间来判断资源在两次请求期间是否有过修改,如果没有修改,则命中协商缓存,浏览器从缓存中获取资源;如果有过修改,则服务器返回资源,同时返回新的Last-Modified时间
      • 具体请求过程
        • 客户端第一次访问服务端资源时,服务端会返回请求的对应资源,并在响应头加上Last-Modified字段,表示这个资源再服务端上最后一次修改时间
        • 当客户端再次请求该资源时,会在请求头加上if-Modified-Since,这个值为上一次服务端返回的Last-Modiffied时间
        • 当服务端收到请求时,会将if-Modified-Since的时间和服务端资源的最后修改时间进行对比,若一致,则命中缓存,返回304,反之则返回资源,并更新Last-Modified
    • 文件摘要(ETag):唯一标识
      • 有些情况下使用最后修改时间来判断是否改动是不够的
      • 存在周期性重写某些资源,但资源的实际内容并无变化
      • 被修改的信息并不重要,如注释等
      • Last-Modified无法精确到毫秒,但有些资源的更新频率小于一秒
      • 为解决这些问题,http允许用户对资源打上标签(ETag)来区分两个相同路径获取的资源内容是否一致。通常会采用MD5等密码散列函数对资源编码得到标签(强验证器);或者通过版本号等方式,如W/”v1.0”(W/表示弱验证器)
      • 具体请求过程
        • 客户端第一次访问服务端资源时,服务端会返回请求的对应资源,并在响应头加上ETag,这个值是根据资源生成的唯一标识(字符串),只要服务端认为资源有变化,则ETag就必须有变化,客户端将资源连同ETag一并缓存
        • 当客户端再次请求资源时,会在请求头加上if-None-Match,值为服务端返回的ETag值
        • 服务端收到请求后,会根据请求的资源重新生成ETag,并跟if-None-Match进行比较,若一致则命中缓存,反之返回新资源和新的ETag
  • Last-Modified,If-Modified-SinceETag,If-None-Match一般同时启用,这是为了处理Last-Modified不可靠的情况。
last-modified & if-Modified-Since

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

etag & if-None-Match

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

二、浏览器请求资源的过程

2.1 浏览器缓存步骤

  • 浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个css文件,如果浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个css,连请求都不会发送到网页所在服务器;
  • 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回(304),但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源;
  • 强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器
  • 当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

三、vue解决发版浏览器缓存问题

3.1 产生的原因

  • 每次生成环境发版,客户的浏览器经常还停留在旧的资源,未及时更新到新资源,这普遍是浏览器缓存造成的。

3.2 解决方案

// vue.config.js(vue2)
const timestamp = new Date().getTime();
module.exports = defineConfig({
  configureWebpack: {
    output: {
      // 修改输出js目录名及文件名
      filename: `js/[name]-test-${timestamp}.js`,
      chunkFilename: `js/[name]-test-${timestamp}.js`,
    },
  },
})
let timeStamp = new Date().getTime();
configureWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            config.output.filename = `js/[name].${timeStamp}.js`
            config.output.chunkFilename = `js/[name].${timeStamp}.js`
         }
    },
css: {
        extract: { 
            ignoreOrder: true,
            filename: `css/[name].${timeStamp}.css`,
            chunkFilename: `css/[name].${timeStamp}.css`,
        }
    },  

浏览器缓存(强缓存、协商缓存)及解决发版浏览器缓存问题

参考: