内容提要
本文以 Koa2 服务器为例讲解浏览器 HTTP 缓存机制。缓存分为强缓存和协商缓存:强缓存由浏览器本地判断是否过期,命中时返回 200 并直接读取内存或磁盘;协商缓存需向服务器发送请求,命中时返回 304。相关头字段优先级为 pragma > cache-control > expires,if-none-match > if-modified-since。更新强缓存资源需修改 URL,如添加版本号或内容 hash。
延伸解读
强缓存与协商缓存的性能权衡
强缓存命中时浏览器直接读取本地缓存,无需与服务器通信,返回200状态码,性能收益最高。协商缓存则需发送请求由服务器验证,命中时返回304,仍有一次网络往返。因此,强缓存适用于更新频率低的静态资源,而协商缓存适用于需要验证但可能未修改的资源。实际项目中,通常对带hash的静态资源设置长期强缓存,对HTML等入口文件使用协商缓存,以平衡性能与更新需求。
缓存头字段的优先级与兼容性
强缓存相关头字段的优先级为pragma > cache-control > expires。pragma和cache-control的no-cache会禁用强缓存,强制走协商缓存。expires依赖绝对时间,易受客户端与服务器时间不同步影响;cache-control的max-age使用相对时间,更可靠。协商缓存中,if-none-match(配合etag)优先级高于if-modified-since(配合last-modified),当两者同时存在时,服务器会忽略if-modified-since。
ETag相比Last-Modified的优势
Last-Modified精确到秒,无法识别1秒内的多次修改,且资源被修改后内容未变时仍会返回新资源,造成浪费。ETag通过资源内容的哈希值或版本号生成唯一标识,能更精确地判断资源是否变化。此外,ETag采用弱比较算法,允许内容一致但生成时间等元数据不同的资源被视为相同,提高了缓存验证的灵活性。因此,在需要高精度缓存控制的场景下,ETag是更优选择。
强缓存资源的更新策略
强缓存资源在过期前不会向服务器发起请求,因此更新资源必须改变其URL。常见做法是在URL中嵌入版本号或内容哈希,如“/v1-0-0/sunset.jpg”或“/5bed2702-sunset.jpg”。当资源更新时,修改引用该资源的HTML中的URL,由于HTML本身通常不设强缓存,每次访问都会获取最新版本,从而间接更新强缓存资源。这种策略确保了客户端能及时获取新资源,同时最大化利用缓存。
Q&A
浏览器HTTP缓存分为哪两种?它们有什么区别?
浏览器HTTP缓存分为强缓存和协商缓存。强缓存由浏览器本地判断缓存是否过期,未过期则直接读取内存或磁盘缓存,不发送请求,命中时返回200状态码;协商缓存需要向服务器发送请求,由服务器判断缓存是否过期,未过期则返回304状态码,浏览器读取本地缓存。强缓存优先级高于协商缓存,且收益更高,因为协商缓存多了一次请求。
强缓存相关的头部字段有哪些?它们的优先级是怎样的?
强缓存相关的头部字段有pragma、cache-control和expires。优先级为pragma > cache-control > expires。pragma只能设置为no-cache,效果与cache-control: no-cache一致,即禁用强缓存。expires是HTTP1.0的字段,表示资源的过期时间,但受客户端和服务器时间差影响。cache-control是HTTP1.1的字段,常用max-age指定相对过期时间,优先级高于expires。
协商缓存中if-modified-since和if-none-match有什么区别?为什么有了last-modified还需要etag?
if-modified-since和last-modified是HTTP1.0的协商缓存字段,基于时间比较,精确到秒;if-none-match和etag是HTTP1.1的字段,基于资源唯一标识符(如哈希值)比较。if-none-match优先级高于if-modified-since。需要etag的原因:1. last-modified精确到秒,无法反映1秒内的修改;2. 资源多次修改但内容不变时,last-modified会变,而etag不变,可避免不必要的传输;3. etag更灵活,支持弱比较。
如何更新配置了强缓存的资源?
更新强缓存资源需要修改资源的URL,因为浏览器在强缓存未过期时不会请求服务器。常见方法是在URL中添加版本号(如/v1-0-0/sunset.jpg)或内容hash值(如/5bed2702-557a-sunset.jpg)。同时,引用该资源的页面(如HTML)不能使用强缓存,以便每次请求都能获取最新的URL。
在Koa2中如何配置强缓存?请举例说明。
在Koa2中,可以通过设置响应头来配置强缓存。例如,在处理图片的路由中,使用ctx.response.set('expires', new Date(Date.now() + 2 * 60 * 1000).toString())设置过期时间为2分钟后;或者使用ctx.response.set('cache-control', 'max-age=60')设置缓存有效期为60秒。注意优先级:pragma > cache-control > expires。
协商缓存的判定流程是怎样的?
当浏览器请求资源时,如果未命中强缓存,且上次请求返回了协商缓存相关响应头(如last-modified或etag),则浏览器会在请求头中带上if-modified-since或if-none-match发送给服务器。服务器比较这些值与资源的最后修改时间或etag,如果未过期则返回304状态码,浏览器使用本地缓存;如果过期则返回200状态码和最新资源,并更新响应头中的last-modified或etag。