web 前端基础:浏览器的缓存机制


web 前端基础:浏览器的缓存机制


前言

掌握缓存策略,对提高前端页面的性能有非常大的帮助,同时也能减少服务端的压力。

同时,这也是前端面试中常问的问题,不管怎么样,这系列知识点在实战应用中很重要,需要理解透彻。

下面讲下,缓存中的强缓存协商缓存。(备注,服务端均由 Express 提供服务)


web 前端基础:浏览器的缓存机制


强缓存

Expires

服务端给客户端设置的 Expires 过期时间响应头,客户端下次再次请求时,会通过本地时间和该时间做对比。

如果本地时间大于 Expires 设置的时间,将重新向服务端获取资源。

下图,服务器设置了1小时的缓存,当客户端再次请求资源时,http 状态码为 200,但能看到 from memory cache 的提示(证明资源文件是从本地拿的):

web 前端基础:浏览器的缓存机制

Expires 示意

代码示意:

express.static 是由 serve-static 提供基础功能的,帮助我们快速设置缓存相关选项。

这里着重看下红框内容,如何设置 Expires 响应头:

web 前端基础:浏览器的缓存机制

express.static 配置


Expires 似乎解决了缓存问题,但光光靠它远远不够,最起码我们现在看到他有这些不足:

  • 服务端需要设置一个固定的 Date 时间
  • 强依赖客户端时间,我们知道本地时间是可以修改的,会导致缓存失效等问题



题外话:

这里注意到 UTC 时间格式,主要为了区别 GMT,如果没有设置的话,将是这样(多 8 个小时):

web 前端基础:浏览器的缓存机制


另外,关于时区 timezone 问题,可以参考 sequelize moment-timezone 之间的处理(题外话,可能部分同学会遇到类似问题,这里带到下):

web 前端基础:浏览器的缓存机制

sequelize 时区处理


web 前端基础:浏览器的缓存机制

sequelize 时区处理



Cache-Control

当我们开启 express static 的 cacheControl 默认配置,能看到 Expires 失效了。

web 前端基础:浏览器的缓存机制

Cache-Control 默认开启


web 前端基础:浏览器的缓存机制

覆盖 Expires


默认 max-age 为 0 ,但这已经证明一点:Cache-Control 的优先级高于 Expires。

我们可以对 max-age 专门设置一个增量的缓存时间:

web 前端基础:浏览器的缓存机制

设置 maxAge

这样客户端每次请求在这个时间范围内,就属于缓存,也就不会向服务器请求资源了。



缓存参数

Cache-Control 还涉及 no-cache/no-store 和 public/private 等具体参数:

  • public:整个网络请求中的任何中间层都可以缓存资源
  • private:对比 public,只有客户端可以缓存资源
  • no-cache:交给服务端判断是否要缓存,跳过强缓存,走入下一环节:协商缓存
  • no-store:不缓存,每次都从服务器获取资源

协商缓存

200 和 304

首先要搞清一个问题,什么时候浏览器请求资源的 http 状态码会返回 200 或者 304。

上述的强缓存都会查看本地环境,验证缓存状态,如果没过期,也会响应 200,只是要注意后面的备注信息(from disk、from memory)。

相反,如果设置了 no-cache 等协商缓存,本地缓存就会忽略,向服务器验证资源是否有更新,没更新则返回 304。


web 前端基础:浏览器的缓存机制

304 http 状态码示意


Last-Modified

Last-Modified 是服务器端设置的响应头,返回请求资源对应的服务器资源文件的修改时间戳。用于验证资源文件是否被修改过。精度比 ETag 低。


web 前端基础:浏览器的缓存机制

Last-Modified 修改文件后,http 状态码


示例

注意,express 中:默认 Last-Modified 和 Etag 是打开的。

首次请求时,如果服务器设置了 Last-Modified 请求头,则会把请求资源文件的最后次修改时间戳返回:

web 前端基础:浏览器的缓存机制

Last-Modified 示意

当我尝试服务器端修改该文件后,再次请求改文件,时间戳为最新一次,可以对比前后时间的不同

web 前端基础:浏览器的缓存机制

注意响应时间的更改

当本地没有缓存时,会向服务器请求资源,同时会增加额外的 If-Modified-Since 请求头,以让服务器做判断。

如果服务器资源没有更新过,则会响应 304 状态码,并不会发送资源文件(能看到没有 Content-Length):

web 前端基础:浏览器的缓存机制

反之,如果服务器资源文件更新过,则请求头中的

If-Modified-Since 和服务器资源文件的时间戳将会不一致,服务器判断后会响应资源。同时服务器将响应 200 状态码,且发送资源文件:

web 前端基础:浏览器的缓存机制


缺点

服务器资源文件管理问题,导致服务器缓存失效:

因为服务器资源文件只要更新/修改过,其文件时间都会更新。如果因为发布,或者其他原因导致这样的变化,则会让服务器响应没必要的更新。(因为文件内容没有变化过)


Etag

弥补 Last-Modified 的一些不足之处,为服务器资源文件生成一个"指纹"值,用于精确对比文件更新状态。


首次请求后,服务器会给客户端一个 Etag 值,下次请求时将以 If-None-Match 向服务器传送客户端 tag 值,如果服务器判断一致,则相应 200,否则 304。

上例中,已经的图例中已经涉及了 Etag 场景,注意对比两个 Etag "指纹"值:

web 前端基础:浏览器的缓存机制


几种刷新操作对比


web 前端基础:浏览器的缓存机制

几种刷新操作对比


总结

简单说了几种缓存相关的概念,具体运用还是要具体分析,配合前端工程化一系列的构建输出,友好的和缓存机制共存是个时刻要磨合的。(大厂都有一套"完美"的最佳实践,但对于我来说还需要积累经验)


分享到:


相關文章: