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

关于浏览器缓存我知道多少 #10

Open
axuebin opened this issue Oct 18, 2017 · 2 comments
Open

关于浏览器缓存我知道多少 #10

axuebin opened this issue Oct 18, 2017 · 2 comments

Comments

@axuebin
Copy link
Owner

axuebin commented Oct 18, 2017

在前端开发中,我们在提到性能优化的时候总会提到一点:合理设置缓存。我们该如何从这方面入手来考虑提高网站性能呢?


前言

我们都知道 HTML5 引入了应用程序缓存,可以在没有网络的情况下进行访问,同时,HTML5 还引入了 storage 本地存储。这些都属于应用缓存。

本篇文章主要内容是和浏览器缓存相关的,也可以说是 HTTP 缓存。

什么是浏览器缓存

MDN 上是这样解释浏览器缓存的:

A browser cache holds all documents downloaded via HTTP by the user ... without requiring an additional trip to the server.

意思就是,浏览器缓存保存着用户通过 HTTP 获取的所有资源,再下一次请求时可以避免重复向服务器发出多余的请求。

通俗的说,就是在你访问过一次某个网站之后,这个站点的文字、图片等所有资源都被下载到本地了,下次再访问该网站时判断是否满足缓存条件,如果满足就不用再花费时间去等待资源的获取了。

浏览器缓存的分类

一般来说浏览器缓存可以分为两类:

  • 强缓存
  • 协商缓存(对比缓存)

我们需要知道的是,浏览器在加载资源时,会先判断是否命中强缓存再验证是命中协商缓存

其它的的具体细节,稍后会展开来说。

强缓存

浏览器在加载资源时,会先根据本地缓存资源的 header 中的信息判断是否命中强缓存,如果命中则直接使用缓存中的资源不会再向服务器发送请求。

从图中可以看出,强缓存一般是这样一个流程:

  1. 查看 header 头中的 ExpireCache-control 来判断是否满足规则;
  2. 如果满足规则,就返回缓存的数据;
  3. 如果不满足规则,就向服务器发送请求;
  4. 服务器返回数据;
  5. 将新数据存入缓存。

所以我们主要就是关注 ExpireCache-control 这两个字段。

Expire

同样地,我们看看MDN中如何解释这个字段:

The Expires header contains the date/time after which the response is considered stale.

这个字段包含了一个时间,过了这个时间,响应将会失效。

也就是说,Expire 这个字段表示缓存到期时间,我们来打开一个网站并查看 Response Header 看看这个字段:

Expires:Fri, 27 Oct 2017 07:55:30 GMT

可能在你查看这的时候发现时间不对啊,怎么都已经是过去了 ~

GMT 表示的是格林威治时间,和北京时间相差8小时。

上面的这个时间表示的是 2017年10月27日15:55:30

通过设置 Expire 来设置缓存有一个致命缺点:

可以看出,这个是个绝对时间,也就是说,如果我修改了客户端的本地时间,是不是就会导致判断缓存失效了呢。

Cache-Control

既然不能设置绝对时间,那我就设置个相对时间呗。

HTTP/1.1 中,增加了一个字段 Cache-Control ,它包含一个 max-age 属性,该字段表示资源缓存的最大有效时间,这就是一个相对时间。

Cache-Control:max-age=600

这个表示的就是最大有效时间是 600s ,对的,它的单位是秒。

Cache-Control 除了 max-age 属性之外还有一些属性:

  • no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。
  • no-store:禁止使用缓存,每一次都要重新请求数据。
  • public:默认设置。
  • private:不能被多用户共享。

现在基本上都会同时设置 ExpireCache-ControlCache-Control 的优先级别更高。

协商缓存

当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据请求头中的部分信息来判断是否命中缓存。如果命中,则返回 304 ,告诉浏览器资源未更新,可使用本地的缓存。

从图中可以看出,协商缓存一般是这样一个流程:

  1. 把资源标识,比如 If-Modify-SinceEtag 发送到服务器,确认资源是否更新;
  2. 如果资源未更新,请求响应返回的http状态为 304 并且会显示一个 Not Modified 的字符串,告诉浏览器使用本地缓存;
  3. 如果资源已经更新,返回新的数据;
  4. 将新数据存入缓存。

Last-Modified,If-Modified-Since

浏览器第一次请求资源的时候,服务器返回的 header 上会带有一个 Last-Modified 字段,表示资源最后修改的时间。

Last-Modified: Fri, 27 Oct 2017 07:55:30 GMT

同样的,这是一个 GMT 的绝对时间。

当浏览器再次请求该资源时,请求头中会带有一个 If-Modified-Since 字段,这个值是第一次请求返回的 Last-Modified 的值。服务器收到这个请求后,将 If-Modified-Since 和当前的 Last-Modified 进行对比。如果相等,则说明资源未修改,返回 304,浏览器使用本地缓存。

well,这个方法也是有缺点的:

  • 最小单位是秒。也就是说如果我短时间内资源发生了改变,Last-Modified 并不会发生变化;
  • 周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为。

所以,后来又引入一个 Etag

Etag

Etag 一般是由文件内容 hash 生成的,也就是说它可以保证资源的唯一性,资源发生改变就会导致 Etag 发生改变。

同样地,在浏览器第一次请求资源时,服务器会返回一个 Etag 标识。当再次请求该资源时, 会通过 If-no-match 字段将 Etag 发送回服务器,然后服务器进行比较,如果相等,则返回 304 表示未修改。

**Last-ModifiedEtag 是可以同时设置的,服务器会优先校验 Etag,如果 Etag 相等就会继续比对 Last-Modified,最后才会决定是否返回 304。 **

总结

当浏览器再次访问一个已经访问过的资源时,它会这样做:

  1. 看看是否命中强缓存,如果命中,就直接使用缓存了;
  2. 如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存;
  3. 如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存;
  4. 否则,返回最新的资源。
@axuebin
Copy link
Owner Author

axuebin commented Oct 30, 2017

@imqinchao
Copy link

从图中可以看出的图 在哪里

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

3 participants
@axuebin @imqinchao and others