Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浏览器缓存机制 #36

Open
ChuChencheng opened this issue Feb 25, 2020 · 0 comments
Open

浏览器缓存机制 #36

ChuChencheng opened this issue Feb 25, 2020 · 0 comments

Comments

@ChuChencheng
Copy link
Owner

思维导图

深入理解浏览器的缓存机制

跟着这篇文章整理的

浏览器缓存

缓存位置

1. Service Worker

  • 基于 Web Worker 独立的线程
  • 可自由控制缓存的文件,持久离线缓存
  • install 事件中缓存文件
  • fetch 事件中拦截网络请求,判断是否命中缓存
  • 没有命中缓存则需要调用 fetch 函数

2. Memory Cache

内存中的缓存。

  • 主要包含已下载的资源,如样式、图片等
  • 读取速度比磁盘快
  • 持续性短,关闭页面则缓存被释放
  • 容量不多
  • preloader 相关指令,如 <link rel="prefetch">

3. Disk Cache

硬盘中的缓存。

  • 存储任意内容
  • 容量大
  • 可持续保存

关于哪些文件会缓存在内存、哪些在硬盘中:

  • 大文件大概率在硬盘中
  • 系统内存使用率高时,文件优先存在硬盘

4. Push Cache

  • HTTP/2 的内容
  • 以上三种缓存未命中才会被使用
  • 只在会话(Session)中存在,会话结束即释放,缓存时间短

缓存类型

浏览器根据请求资源时返回的响应头来判断资源是否该缓存;
根据请求头与响应头判断如何使用缓存。

1. 强缓存

不会发起请求,直接从缓存中读取资源,在 Devtool 中可以看到资源请求返回 200 状态码, Size 显示 from disk cache 或 from memory cache

通过设置 ExpiresCache-Control 两个 header 实现

Expires

  • 用于指定资源到期时间
  • 浏览器在到期时间之前可以使用缓存无需再次请求
  • HTTP/1.0 产物,受限于本地时间,如果修改本地时间可能造成缓存失效

Cache-Control

  • 可以在请求头或响应头中设置
  • 可以组合使用多种指令

指令:

  1. public: 客户端与代理服务器都可缓存,例如 Server -> Proxy -> Browser ,设置了 public 后, Proxy 也可以缓存资源,下次 Browser 请求同一个资源时, Proxy 可直接返回缓存的资源而不用向 Server 请求
  2. private: 只有客户端可以缓存资源
  3. no-cache: 客户端在使用缓存之前,会经过协商缓存来判断资源是否与服务端一致,即会发起请求
  4. no-store: 不缓存资源,即不使用强缓存,也不使用协商缓存
  5. max-age: 缓存内容将在指定秒数后失效
  6. s-maxage: 只在代理服务器中生效,优先级高于 max-age ,如果存在则会覆盖 max-age 和 Expires
  7. max-stale: 表示客户端愿意接收一个已经过期的响应,在过期后指定秒数内可使用缓存
  8. min-fresh: 表示客户端不愿接受新鲜度不高于当前 age + min-fresh 设定秒数 的响应

两者对比

Expires 与 Cache-Control 区别在于,前者是 HTTP/1.0 的产物,二者同时存在时, Cache-Control 优先级更高。在不支持 HTTP/1.1 的环境下则 Expires 有效。现阶段 Expires 只是一种兼容的写法。

强缓存是基于时间来判断是否使用缓存,而不关心服务端文件是否已经更新,这可能导致拿不到最新的文件。

2. 协商缓存

在强缓存失效后,就会使用协商缓存;
协商缓存会发起请求,由服务器根据缓存标识决定是否使用缓存;
如果协商缓存有效,则响应 304 状态码, Not Modified 并且不会发送资源,否则会返回 200 状态码与请求资源结果。

协商缓存可以通过两对 headers 实现:

Last-Modified 和 If-Modified-Since

Last-Modified 记录文件在服务器上的最后修改时间,当浏览器请求这个文件时,会带上 Last-Modified 请求头。

当浏览器下次请求这个资源时,如果有 Last-Modified 请求头,则会添加 If-Modified-Since ,值为 Last-Modified 的值。

当服务器收到请求时,会将浏览器发送的 If-Modified-Since 值与请求资源的最后修改时间进行对比,如果没有变化,则返回 304 使用缓存;如果小于服务器中这个资源的最后修改时间,则表示文件有更新,返回新的资源和 200 。

缺陷:

  • 打开文件而不进行修改的话,虽然文件内容没变,但最后修改时间会被改变,导致 Last-Modified 被修改,服务端会发送相同的资源
  • Last-Modified 精确到秒,如果在 1 秒内修改了文件,则服务端还是会判断命中缓存,而不返回新的资源

ETag 和 If-None-Match

ETag 是服务端返回的当前资源的唯一标识(服务端生成),只要资源有变化,则 ETag 会重新生成。

浏览器下次请求缓存文件时,会把上一次的 ETag 放入 If-None-Match 中,服务端接收到请求后,会对比 If-None-Match 与服务器上文件的 ETag ,来判断该资源是否有更新。

ETag 的优势在于精确度高于以秒为精度的 Last-Modified

其缺点则是服务端要计算 hash 要耗费性能

在优先级上,服务器优先考虑 ETag

应用场景

没有一个绝对通用的缓存策略,只有根据不同的场景来使用不同的策略才能发挥缓存的作用。

1. 频繁变动的资源

对于频繁变动的资源,使用 Cache-Control: no-cache ,这样,浏览器每次请求此类资源都会发起请求,配合 ETag 或 Last-Modified 来验证缓存资源是否有效。

此做法不能节省请求,但可以减少响应数据的大小。

2. 不常变化的资源

对于这类资源通常使用 Cache-Control: max-age=31536000 ,配置一个较大的 max-age ,这样浏览器会使用强缓存,而不会发起请求。

如果遇到文件更新,则通过改变文件名的方式更新,旧的资源则因 URL 不同,不会再被请求而弃用。

这个场景在平时用得比较多,例如线上引用的 JS、CSS 等资源,在文件名后面都会加上一个 hash 值。

用户触发缓存行为

  • 打开网页: 查找 disk cache ,如果未命中,则发送请求
  • 普通刷新 F5: 因为页面没有关闭,因此会优先使用 memory cache ,其次 disk cache
  • 强制刷新 Ctrl + F5: 请求头部带有 Cache-Control: no-cache

关于缓存的其他知识

这部分与浏览器或许不是很相关

缓存的特征

1. 命中率

衡量缓存是否有效的重要指标

命中率=返回正确结果数/请求缓存次数

2. 最大元素

能存放缓存的最大个数,超过这个数量,则会触发清空策略

合理设置最大元素值可提高缓存的命中率

3. 清空策略

  • FIFO (First In First Out) 先进先出策略,在数据时效性优先的场景下可使用
  • LFU (Less Frequently Used) 最少使用策略,根据元素被使用次数判断,在保证高频数据有效性的场景下可使用
  • LRU (Least Recently Used) 最近最少使用策略,根据元素最后被使用的时间判断,在保证热点数据有效性的场景下可使用

一些简单的策略:

  • 根据过期时间,清除过期时间最长的元素
  • 根据过期时间,清除即将过期的元素
  • 随机清除
  • 根据关键字或内容长短清除

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant