存勖闲

2018-12-26 00:07

浏览器缓存

本文作者:IMWeb 存勖闲 原文出处:IMWeb社区 未经同意,禁止转载

原文链接:http://www.cun-xu.cn/index.php/2018/12/26/浏览器缓存/

今天我们来说一下浏览器缓存的问题,缓存可以减少网络IO的消耗,提高访问速度。浏览器缓存是一种操作简单、效果显著的前端性能优化手段。

关于缓存的头部字段包括:

cache-control(缓存头)

  • 每个资源都可通过cache-controlHTTP标头定义其缓存策略
  • cache-control指令控制谁在什么条件下可以缓存响应以及可以缓存多少
可缓存性

public: 即使它有相关联的http身份验证,甚至响应状态代码通常无法缓存,也可以缓存响应。大多数情况下,public不是必需的,因为明确的缓存信息(例如max-age)已表示响应是可以缓存的。

private: 浏览器可以缓存private响应。不过,这些响应通常只为单个用户缓存,因此不允许任何中间缓存对其进行缓存。例如,用户的浏览器可以缓存包含私人信息的HTML网页,但CDN却不能进行缓存。

no-cache: 表示必须先与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一网址的请求。因此,如果存在合适的验证令牌(Etag),no-cache会发起往返通信来验证缓存的响应,但如果资源未发生变化,则可避免下载。有的时候只设置no-cache防止缓存还是不够保险,还可以加上private指令,将过期时间设为过去的时间。

到期

max-age=(seconds) 指令指定从请求的时间开始,允许获取的响应被重用的最长时间(单位:秒)。例如,"max-age=60"表示可在接下来的60秒缓存和重用响应。

s-maxage=(seconds):同max-age,只用于共享缓存(比如CDN缓存) 比如,当s-maxage=60时,在这60秒钟,即使更新了CDN的内容,浏览器也不会进行请求。也就是说max-age用与普通缓存,而s-maxage用于代理缓存,如果存在s-maxage,则会覆盖掉max-ageExpiresheader。

max-stale=(seconds) 为发起端设置,即使缓存已经到期,但在max-stale设置的时间内还可以使用过期的缓存。

重新验证

must-revalidate 缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。

proxy-revalidatemust-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。

其他

no-store 直接禁止浏览器以及所有中间缓存存储任何版本的返回响应,例如,包含个人隐私数据或银行业务数据的响应。每次用户请求该资源时,都会向服务器发送请求,并下载完整的响应。

no-transform 不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。

Pragma

当该字段值为no-cache的时候。会告诉浏览器不要对该资源缓存,及每一次都得向服务器发送一次响应。

res.serHeader('Pragma','no-cache')                                //禁止缓存
res.setHeader('Cache-Control','public,max-age=120')   //2分钟

通过Pragma来禁止缓存,通过Cache-Control设置两分钟缓存,但是重新访问我们会发现浏览器会再次发起一次请求,说明了Pragma的优先级高于Cache-Control

Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires = max-age + 请求时间,需要和Last-modified结合使用。但在上面我们提到过,cache-control的优先级更高。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前可以直接从浏览器缓存取数据,而无需再次请求。

Last-Modified 上次修改时间

服务器端文件的最后修改时间,需要和catch-control共同使用,是检查服务器资源是否更新的一种方式。当浏览器再次进行请求时,会向服务器传送If-Modified-Since报头,询问Last-Modified时间点之后资源是否被修改过。如果没有修改,则返回码为304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为200,资源为服务器最新资源。

Etag 数据签名

根据试题内容生成一段哈希字符串,标识资源的状态,由服务器产生。浏览器会将这串字符串传回服务器,验证资源是否已被修改,如果没有修改,过程如下: 使用Etag可以解决Last-modified存在的一些问题:

  • 某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新
  • 如果某些资源修改非常频繁,在秒一下的时间内进行修改,而last-modified只能精确到秒。
  • 一些资源的最后修改时间改变了,但是内容没改变,使用Etag就认为资源还是没有修改的

根据查到的资料显示,缓存头的优先级是这样的:

Pragma > Cache-Control > Expires > Etag > Last-Modified

缓存策略

至于缓存策略的制定,我们可以参照Google官方的这张图

由于图上都是英文,我猜像我这样的菜鸟都看不懂,这里借资料来解释一下:

当我们的资源内容不可复用时,直接为 Cache-Control 设置no-store,拒绝一切形式的缓存;否则考虑是否每次都需要向服务器进行缓存有效确认,如果需要,那么设 Cache-Control 的值为 no-cache;否则考虑该资源是否可以被代理服务器缓存,根据其结果决定是设置为 private 还是public;然后考虑该资源的过期时间,设置对应的 max-ages-maxage 值;最后,配置协商缓存需要用到的 Etag、Last-Modified等参数。

根据上面的基础知识和解读,我们可以知晓:在制定缓存策略时,需要牢记以下技巧:

  • 使用一致的网址:如果您在不同的网址上提供相同的内容,将会多次获取和存储这些内容。提示:网址区分大小写。
  • 确保服务器提供验证令牌(Etag):有了验证令牌,当服务器上的资源未发生变化时,就不需要传送相同的字节。
  • 确定中间缓存可以缓存哪些资源:对所有用户的响应完全相同的资源非常适合由CDN以及其他中间缓存进行缓存。
  • 为每个资源确定最佳缓存周期:不同的资源可能有不同的更新要求。为每个资源审核并确定合适的max-age
  • 确定最合适您的网站的缓存层次结构:您可以通过为HTML文档组合使用包含内容指纹的资源网址和短时间或no-cache周期,来控制客户端获取更新的速度。
  • 最大限度减少搅动:某些资源的更新比其他资源频繁。如果资源的特定部分(例如JavaScript函数或CSS样式集)会经常更新,可以考虑将其代码作为单独的文件提供。这样一来,每次获取更新时,其余内容(例如变化不是很频繁的内容库代码)可以从缓存获取,从而最大限度减少下载的内容大小

文章的最后我们还要说一下内存缓存(from memory cache)和硬盘缓存(from disk cache)。

内存缓存

内存缓存有两个特点,分别是快速读取和时效性。

  • 快速读取:内存换粗会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以便下次运行时的快速读取
  • 时效性:一旦该进程关闭,则该进程的内存会被清空

硬盘缓存

硬盘缓存- 则是将缓存直接写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

那么哪些文件会被放入内存中呢?

事实上,这个划分规则,一直以来是没有定论的。不过想想也可以理解,内存时有限的,很多时候需要先考虑即时呈现的内存余量,再根据具体的情况决定分配给内存和磁盘的资源量的比重——资源存放的位置具有一定的随机性。

虽然划分规则没有定论,但根据日常开发中观察的结果,我们至少可以总结出这样的规律:资源存不存内存,浏览器秉承的是“节约原则”。我们发现,Base64格式的图片,几乎永远可以被塞进memory cache,这可以视作浏览器为节省渲染开销的“自保行为”;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的几率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。

浏览器读取缓存的顺序为memory –> disk

好啦,着急忙慌整完了,正好零点,碎觉碎觉。 明天还要考试,关老爷保佑!!!

参考文章: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn http://www.alloyteam.com/2016/03/discussion-on-web-caching/#prettyPhoto

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。