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

2019/01/14 - 从前端的角度理解缓存 #30

Open
wieve opened this issue Jan 14, 2019 · 3 comments
Open

2019/01/14 - 从前端的角度理解缓存 #30

wieve opened this issue Jan 14, 2019 · 3 comments

Comments

@wieve
Copy link
Contributor

wieve commented Jan 14, 2019

缓存的概念分很多种,本次讨论的主要就是前端缓存中的Http缓存。

缓存是怎么回事

前端发送请求主要经历以下三个过程,请求->处理->响应。
如果有多次请求就需要重复执行这个过程。

重复请求的过程

以下是一个重复请求的流程图:

重复请求

从以上的流程图可以看书,如果用户重复请求同一资源的话,会对服务器资源造成浪费,服务器重复读取资源,发送给浏览器后浏览器重复下载,造成不必要的等待与消耗。

缓存读取的过程

缓存读取就是浏览器在向服务器请求资源之前,先查询一下本地缓存中是否存在需要的资源,如果存在,那便优先从缓存中读取。当缓存不存在或者过期,再向服务器发送请求。

缓存读取

如何开启Http缓存并对缓存进行设置,是本次讨论的关键。

缓存的类型

浏览器有如下常见的几个字段:

  1. expires: 设置缓存过期的时间
  2. private: 客户端可以缓存
  3. public: 客户端和代理服务器都可缓存
  4. max-age=xxx: 缓存的内容将在 xxx 秒后失效
  5. no-cache: 需要使用对比缓存来验证缓存数据
  6. no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发
  7. last-modified: 内容上次被修改的时间
  8. Etag: 文件的特殊标识

强制缓存和协商缓存

缓存方法可以分为强制缓存与协商缓存。

从字面理解,强制缓存的方式简单粗暴,给cache设置了过期时间,超过这个时间之后cache过期需要重新请求。上述字段中的expirescache-control中的max-age都属于强制缓存。

协商缓存根据一系列条件来判断是否可以使用缓存。

强制缓存优先级高于协商缓存

强制缓存

expires

expires给浏览器设置了一个绝对时间,当浏览器时间超过这个绝对时间之后,重新向服务器发送请求。

Expires: Fri, 04 Jan 2019 12:00:00 GMT

这个方法简单直接,直接设定一个绝对的时间 (当前时间+缓存时间)。但是也存在隐患,例如浏览器当前时间是可以进行更改的,更改之后expires设置的绝对时间相对不准确,cache可能会出现长久不过期或者很快就过期的情况。

cache-control: max-age

为了解决expires存在的问题,Http1.1版本中提出了cache-control: max-age,该字段与expires的缓存思路相同,都是设置了一个过期时间,不同的是max-age设置的是相对缓存时间开始往后多久,因此不存在受日期不准确情况的影响。

但是强制缓存存在一个问题,该缓存方式优先级高,如果在过期时间内缓存的资源在服务器上更新了,客服端不能及时获取最新的资源。

协商缓存

协商缓存解决了无法及时获取更新资源的问题。以下两组字段,都可以对资源做标识,由服务器做分析,如果未进行更新,那返回304状态码,从缓存中读取资源,否则重新请求资源。

last-modify

last-modify告知了客户端上次修改该资源的时间,

Last-Modified: Wed, 02 Jan 2019 03:06:03 GMT

浏览器将这个值记录在if-modify-since中(浏览器自动记录了该字段信息),下一次请求相同资源时,与服务器返回的last-modify进行比对,如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

last-modify以秒为单位进行更新,如果小于该单位高频进行更新的话,不适合采用该方法。

ETag

ETag是对资源的特殊标识

Etag: W/"e563df87b65299122770e0a84ada084f"

请求该资源成功之后,将返回的ETag存入if-none-match字段中(浏览器自动记录了该字段信息),同样在请求资源时传递给服务器,服务器查询该编码对应的资源有无更新,无更新返回304状态,更新返回200并重新请求。

以下有个小例子,查询书籍更新:

当书籍信息查询之后,再次查询,服务器根据资源的ETag查询得知该资源没有进行更新,返回304状态码。

书籍信息(旧)

更新返回的数据信息,再次查询,返回200状态码,重新进行请求:

书籍信息(新)

从返回的Request Headers可以看出,再次请求时,浏览器自动发送了If-Modified-SinceIf-None-Match两个字段,浏览器根据这两个字段中(If-None-Match 优先级大于 If-Modified-Since)来判断是否修改了资源。

image

ETag如何计算

ETag是针对某个文件的特殊标识,服务器默认采用SHA256算法生成。也可以采用其他方式,保证编码的唯一性即可。

缓存的优先级

根据上文优缺点的比对,可以得出以下的优先级顺序:

Cache-Control > Expires > ETag > Last-Modified

如果资源需要用到强制缓存,Cache-Control相对更加安全,协商缓存中利用ETag查询更新更加全面。

缓存的判断流程

图片来源:浏览器缓存机制详解

缓存存储在哪

disk cache

disk cache为存储在硬盘中的缓存,存储在硬盘中的资源相对稳定,不会随着tab或浏览器的关闭而消失,可以用来存储大型的,需长久使用的资源。

当硬盘中的资源被加载时,内存中也存储了该资源,当下次改资源被调用时,会优先从memory cache中读取,加快资源的获取。

memory cache

memory cache即存储在内存中的缓存,内存中的内容会随着tab的关闭而释放。

当接口状态返回304时,资源默认存储在memory cache中,当页面关闭后,重新打开需要再次请求。

这两种存储方式的区别可以参考该回答

When you visit a URL in Chrome, the HTML and the other assets(like images) on the page are stored locally in a memory and a disk cache. Chrome will use the memory cache first because it is much faster, but it will also store the page in a disk cache in case you quit your browser or it crashes, because the disk cache is persistent.

当您访问chrome中的URL时,页面上的HTML和其他资产(如图像)将本地存储在内存和磁盘缓存中。Chrome将首先使用内存缓存,因为它的速度快得多,但它也会将页面存储在磁盘缓存中,以防您退出浏览器或它崩溃,因为磁盘缓存是持久的。

为什么有的资源一会from disk cache,一会from memory cache

三级缓存原理

  1. 先去内存看,如果有,直接加载
  2. 如果内存没有,择取硬盘获取,如果有直接加载
  3. 如果硬盘也没有,那么就进行网络请求
  4. 加载到的资源缓存到硬盘和内存,下次请求可以快速从内存中获取到

为什么有的请求状态码返回200,有的返回304

200 from memory cache

不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当关闭进程后,也就是浏览器关闭以后,数据将不存在。

但是这种方式只能缓存派生资源。

200 from disk cache

不访问服务器,直接读缓存,从磁盘中读取缓存,当关闭进程时,数据还是存在。

这种方式也只能缓存派生资源

304 Not Modified

访问服务器,发现数据没有
更新,服务器返回此状态码。然后从缓存中读取数据。

薄荷应用

举一个简单的小🌰,以薄荷的减肥群页面为讨论对象,查看一下资源加载的情况:

薄荷图片缓存

这些图片都是从硬盘中读取,因为没有在内存中获取到响应的资源,当我们刷新页面时,这个资源因为从硬盘中读取时,也存储到了内存中,再次获取就是从内存中获取了:
薄荷图片缓存2

当我们没有关闭页面时,内存中的资源始终存在,重新打开则内存释放。

CDN缓存

CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control: max-age的字段来设置CDN边缘节点数据缓存时间。

当客户端向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向源站发出回源请求,从源站拉取最新数据,更新本地缓存,并将最新数据返回给客户端。

如何合理应用缓存

强制缓存优先级最高,并且资源的改动在缓存有效期内都不会对缓存产生影响,因此该方法适用于大型且不易修改的的资源文件,例如第三方CSS、JS文件或图片资源,文件后可以加上hash进行版本的区分。建议将此类大型资源存入disk cache,因为存在硬盘中的文件资源不易丢失。

协商缓存灵活性高,适用于数据的缓存,根据上述方法的对比,采用Etag标识进行对比灵活度最高,并考虑将数据存入内存中,因为内存加载速最快,并且数据体积小,不会占用大量内存资源。

广而告之

本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。

欢迎讨论,点个赞再走吧 。◕‿◕。 ~

@renyijiu
Copy link

如和开启Http缓存并对缓存进行设置,错别字哈

@wusb
Copy link
Contributor

wusb commented Jan 17, 2019

@renyijiu 已修改,多谢大佬提醒。

@paveew
Copy link

paveew commented Jun 17, 2019

从以上的流程图可以看书,错别字 report

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

No branches or pull requests

4 participants