跳到主要内容

浏览器协商缓存详解

什么是协商缓存

协商缓存是浏览器缓存机制的一种,当强缓存未命中时,浏览器会向服务器发送请求,由服务器判断资源是否有更新。如果资源未更新,服务器返回304状态码,浏览器继续使用本地缓存;如果资源已更新,服务器返回200状态码和新资源。

协商缓存解决了强缓存可能导致的资源更新问题,是一种更灵活的缓存策略。

协商缓存的控制方式

浏览器的协商缓存主要通过两对HTTP头字段来控制:Last-Modified/If-Modified-SinceETag/If-None-Match

Last-Modified/If-Modified-Since

这是基于时间戳的协商缓存机制。

Last-Modified

Last-Modified是服务器响应头中的字段,表示资源的最后修改时间。

示例:

Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT

If-Modified-Since

If-Modified-Since是浏览器请求头中的字段,它的值来自服务器上一次返回的Last-Modified值。当浏览器再次请求该资源时,会将这个时间戳发送给服务器。

示例:

If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT

工作原理:

  1. 浏览器第一次请求资源,服务器返回资源和Last-Modified
  2. 浏览器再次请求该资源时,发送包含If-Modified-Since头的请求
  3. 服务器比较If-Modified-Since和资源的当前最后修改时间
    • 如果资源未修改,返回304状态码,不返回资源内容
    • 如果资源已修改,返回200状态码和新资源,并更新Last-Modified

Last-Modified的局限性:

  • 只能精确到秒级,如果资源在一秒内多次修改,无法准确识别
  • 某些情况下,资源的最后修改时间会改变,但内容并未改变,导致缓存失效
  • 服务器无法感知文件的具体变化,只能通过时间戳判断

ETag/If-None-Match

这是基于实体标签(Entity Tag)的协商缓存机制,比基于时间戳的机制更精确。

ETag

ETag是服务器响应头中的字段,它是资源内容的唯一标识符,通常是根据资源内容计算的哈希值。

示例:

ETag: "686897696a7c876b7e"

If-None-Match

If-None-Match是浏览器请求头中的字段,它的值来自服务器上一次返回的ETag值。当浏览器再次请求该资源时,会将这个实体标签发送给服务器。

示例:

If-None-Match: "686897696a7c876b7e"

工作原理:

  1. 浏览器第一次请求资源,服务器返回资源和ETag
  2. 浏览器再次请求该资源时,发送包含If-None-Match头的请求
  3. 服务器比较If-None-Match和资源的当前ETag
    • 如果两者匹配(资源未修改),返回304状态码,不返回资源内容
    • 如果两者不匹配(资源已修改),返回200状态码和新资源,并更新ETag

ETag的优势:

  • 基于资源内容生成,能准确识别资源的微小变化
  • 不受时间戳限制,可以识别一秒内的多次修改
  • 可以自定义生成算法,更灵活

Last-Modified与ETag的优先级

当HTTP响应头中同时存在Last-ModifiedETag时,ETag的优先级高于Last-Modified。服务器通常会同时支持这两种机制,以提供更可靠的缓存验证。

协商缓存的工作流程

  1. 浏览器第一次请求资源,服务器返回资源、Last-Modified/ETag和可选的强缓存头
  2. 强缓存未命中时,浏览器发送包含If-Modified-Since/If-None-Match的请求到服务器
  3. 服务器验证资源是否有更新
    • 如果资源未更新,返回304状态码,浏览器继续使用本地缓存
    • 如果资源已更新,返回200状态码和新资源
  4. 浏览器更新本地缓存

协商缓存与强缓存的结合使用

在实际应用中,强缓存和协商缓存通常结合使用:

  1. 首先检查强缓存是否命中
  2. 如果强缓存未命中,再进行协商缓存验证
  3. 如果协商缓存也未命中,才从服务器获取新资源

这种组合策略既能充分利用强缓存的高性能,又能通过协商缓存确保资源的新鲜度。

协商缓存的应用场景

协商缓存适用于以下场景:

  • 对实时性有一定要求的资源
  • 经常变化但又不是每次请求都变化的资源
  • 需要精确控制缓存失效的场景
  • 静态资源更新时需要及时反映到客户端的场景

如何优化协商缓存

为了提高协商缓存的效率,可以考虑以下优化策略:

  • 合理设置强缓存时间,减少协商缓存的请求次数
  • 使用ETag替代或补充Last-Modified,提高缓存验证的准确性
  • 避免不必要的协商缓存请求,例如对完全动态的资源直接设置no-cache
  • 考虑使用CDN缓存,减轻服务器的协商缓存验证压力

协商缓存的性能影响

虽然协商缓存需要发送请求到服务器,但它的性能影响通常是可接受的:

  • 协商缓存请求的体积很小(只有请求头)
  • 服务器处理协商缓存请求的成本较低
  • 304响应不包含资源内容,节省带宽
  • 相比于重新获取资源,协商缓存仍然能显著提高性能

协商缓存的最佳实践

  • 对静态资源同时设置强缓存和协商缓存
  • 使用文件内容哈希生成ETag,确保内容变化时ETag也变化
  • 合理设置Cache-Control: max-age,平衡性能和资源新鲜度
  • 避免频繁更改不影响内容的文件属性(如权限、所有者等),以免触发不必要的缓存失效
  • 考虑使用ETag的弱验证器(前缀为W/)来处理可能的非实质性内容更改

前端面试中的协商缓存常见问题

1. Last-Modified/If-Modified-Since和ETag/If-None-Match的区别是什么?哪个优先级更高?

参考答案:

  • Last-Modified/If-Modified-Since:基于资源的修改时间进行验证
  • ETag/If-None-Match:基于资源的内容标识(通常是哈希值)进行验证

ETag的优先级高于Last-Modified。当两者同时存在时,浏览器会优先使用ETag进行验证。

2. ETag有什么优势?为什么说它比Last-Modified更可靠?

参考答案:

ETag的主要优势包括:

  1. 精度更高:ETag基于资源内容生成,可以准确识别资源的微小变化
  2. 不受时间戳限制:可以识别一秒内的多次修改
  3. 解决内容未变但时间戳变化的问题:某些情况下,资源内容未变但最后修改时间会改变,导致Last-Modified失效
  4. 自定义生成算法:可以根据业务需求自定义ETag的生成算法,更灵活

由于以上优势,ETag比Last-Modified更可靠,特别是对于需要精确验证的场景。

3. 什么是强协商缓存和弱协商缓存?它们有什么区别?

参考答案:

  • 强协商缓存:使用ETagIf-None-Match进行验证,基于资源内容的精确匹配
  • 弱协商缓存:使用W/ETag(弱ETag)和If-None-Match进行验证,可能基于资源的部分内容或元数据生成

区别

  • 强ETag完全基于资源内容生成,内容变化时ETag一定变化
  • 弱ETag可能基于资源的部分特征生成,相同内容可能有不同的弱ETag,不同内容也可能有相同的弱ETag
  • 弱ETag通常用于可以接受近似匹配的场景,如静态资源的gzip压缩版本

4. 协商缓存的304状态码和200状态码有什么区别?

参考答案:

  • 304 Not Modified:表示资源未修改,浏览器继续使用本地缓存,响应体为空,节省带宽
  • 200 OK:表示资源已修改,服务器返回新资源的完整内容

在协商缓存流程中:

  • 如果资源未修改,服务器返回304状态码
  • 如果资源已修改,服务器返回200状态码和新资源

5. 为什么说协商缓存不能完全替代强缓存?

参考答案:

协商缓存不能完全替代强缓存的原因:

  1. 性能差异:强缓存不需要发送请求,协商缓存需要发送请求到服务器进行验证
  2. 服务器压力:协商缓存会增加服务器的请求处理压力
  3. 网络开销:即使是304响应,也需要消耗一定的网络带宽和时间
  4. 用户体验:强缓存能提供更快的页面加载速度

因此,在实际应用中,通常会结合使用强缓存和协商缓存,先用强缓存减少请求次数,再用协商缓存确保资源新鲜度。

协商缓存经典案例分析

案例1:社交平台的动态内容缓存策略

背景:某社交平台有大量用户生成内容,需要保证内容的实时性,同时优化性能。

解决方案

  1. HTML页面使用协商缓存

    • 对用户个人主页、动态feed等HTML页面,设置Cache-Control: no-cache,强制每次都进行协商缓存
    • 结合ETag实现精确验证
  2. API响应分层缓存

    • 对于用户资料等相对稳定的API响应,设置较短的强缓存时间(如5分钟)+ 协商缓存
    • 对于实时动态等频繁变化的API响应,仅使用协商缓存
  3. 条件请求优化

    • 服务器端优化ETag生成算法,避免不必要的计算开销
    • 对于大型响应,实现部分内容验证机制,减少传输数据量

效果

  • 页面加载速度提升了35%
  • 服务器带宽消耗减少了40%
  • 同时保证了内容的实时性,用户反馈良好

案例2:内容管理系统的资源验证优化

背景:某企业内容管理系统有大量文档、图片等资源,需要在保证资源更新的同时,优化访问性能。

解决方案

  1. 混合验证策略

    • 对小型资源(如文档、小图片),使用ETag进行精确验证
    • 对大型资源(如视频、高清图片),使用Last-Modified进行时间验证,减少服务器计算开销
  2. 缓存元数据

    • 在服务器端缓存资源的ETag和Last-Modified值
    • 避免每次请求都重新计算这些值
  3. 客户端智能缓存

    • 实现客户端缓存策略,根据资源类型和更新频率动态调整验证频率
    • 对于用户明确标记为"已读"的内容,适当延长缓存时间

效果

  • 资源加载速度提升了50%
  • 服务器CPU使用率下降了30%
  • 用户等待时间减少,工作效率提高

总结

协商缓存是浏览器缓存体系中的重要组成部分,通过Last-Modified/If-Modified-SinceETag/If-None-Match两对HTTP头字段,实现了资源的精确验证和缓存控制。协商缓存与强缓存结合使用,可以形成更完善的缓存策略,既能提高性能,又能确保资源的新鲜度。

在实际应用中,我们应该根据资源的特性和需求,合理设置协商缓存策略,同时考虑服务器的处理成本和网络开销,找到性能和资源新鲜度之间的最佳平衡点。

掌握协商缓存的原理和实践,不仅能帮助我们优化Web应用性能,也是前端面试中的高频考点。通过以上常见问题和经典案例的学习,相信你对协商缓存有了更深入的理解。