强缓存和协商缓存原理,及前端如何和服务端一同控制影响浏览器缓存,以及代码实战

时间:2024-03-01 09:56:01
1.强缓存(也称为本地缓存)和协商缓存是Web开发中用于优化页面加载性能的两种主要缓存机制。
  1. 强缓存:
    • 工作原理: 当客户端首次请求资源时,服务器会返回带有缓存控制信息的响应头,如Cache-ControlExpires。这些信息告诉客户端可以在一定时间内直接使用本地缓存而不需要向服务器发起请求。
    • Cache-Control: 使用max-age指定资源的最大缓存时间,例如Cache-Control: max-age=3600表示资源在3600秒内有效。
    • Expires: 指定资源的过期时间,是一个GMT格式的日期字符串,如Expires: Wed, 29 Feb 2024 12:00:00 GMT
    • 优点: 加快页面加载速度,减轻服务器负担。
  2. 协商缓存:
    • 工作原理: 当客户端再次请求资源时,它会发送一个请求头,如If-Modified-SinceIf-None-Match,携带上一次请求时服务器返回的相关信息。服务器根据这些信息判断是否需要返回新的资源,或者告诉客户端直接使用本地缓存。
    • Last-Modified: 服务器在响应头中返回资源的最后修改时间,客户端可通过If-Modified-Since发送上一次获取资源时的修改时间。
    • ETag: 服务器在响应头中返回资源的唯一标识,客户端可通过If-None-Match发送上一次获取资源时的标识。
    • 优点: 减少不必要的数据传输,提高服务器性能。
2.怎么使用

很多前端人看了这些东西,只知道原理,但是完全不知道如何去使用,下面我把强缓存和协商缓存的所有写法都列举出来,并附上相关的解释

1. 强缓存:
a. 使用 Cache-Control 字段的 no-store 指令,表示不缓存:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'Cache-Control': 'no-store'
  }
})
  .then(response => response.text())
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

b. 使用 Cache-Control 字段的 no-cache 指令,表示不直接使用缓存,需要向服务器验证:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'Cache-Control': 'no-cache'
  }
})
  .then(response => response.text())
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

c. 使用 Cache-Control 字段的 max-age 指令,设置缓存最大有效时间:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'Cache-Control': 'max-age=3600' // 缓存1小时
  }
})
  .then(response => response.text())
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

d. 使用 Expires 字段,设置缓存过期时间:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'Expires': new Date(Date.now() + 3600000).toUTCString() // 缓存1小时
  }
})
  .then(response => response.text())
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

2. 协商缓存:
a. 使用 If-None-Match 字段,携带上一次服务器返回的ETag值:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'If-None-Match': 'previous-etag-value'
  }
})
  .then(response => {
    if (response.status === 304) {
      // 服务器返回 304 表示资源未被修改,可以使用缓存
      return getCachedData();
    } else {
      return response.text();
    }
  })
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

b. 使用 If-Modified-Since 字段,携带上一次服务器返回的Last-Modified值:
fetch('/path/to/resource', {
  method: 'GET',
  headers: {
    'If-Modified-Since': 'previous-last-modified-value'
  }
})
  .then(response => {
    if (response.status === 304) {
      // 服务器返回 304 表示资源未被修改,可以使用缓存
      return getCachedData();
    } else {
      return response.text();
    }
  })
  .then(data => {
    // 处理获取到的数据
  })
  .catch(error => {
    console.error('Error:', error);
  });

3.协商缓存使用的这些值,是如何判断他是不是要缓存呢

协商缓存是通过比较客户端发送的条件请求头(例如 If-None-MatchIf-Modified-Since)与服务器上资源的相关信息来判断是否要返回新的资源还是告诉客户端继续使用缓存的一种机制。

  1. ETag(实体标签):
    • 服务器在响应头中生成一个唯一的标识符(通常是哈希值),称为ETag。
    • 当客户端再次请求资源时,它会携带上一次获取的ETag值,放在请求头的 If-None-Match 字段中。
    • 服务器比较客户端提供的ETag值与当前资源的ETag值,如果匹配,服务器返回状态码304,表示资源未被修改,可以使用缓存。

Last-Modified(最后修改时间):

  • 服务器在响应头中提供资源的最后修改时间。
  • 当客户端再次请求资源时,它会携带上一次获取的最后修改时间,放在请求头的 If-Modified-Since 字段中。
  • 服务器比较客户端提供的最后修改时间与当前资源的最后修改时间,如果客户端提供的时间早于资源的最后修改时间,服务器返回状态码304,表示资源未被修改,可以使用缓存。
总而言之,言而总之,这个缓存还是很复杂,只能从实际中慢慢去解析和进行开发了。