jd-smart-fe / shared

共享文档
MIT License
25 stars 4 forks source link

浅析浏览器缓存 #1

Open zhixuanziben opened 7 years ago

zhixuanziben commented 7 years ago

浅析浏览器缓存


众所周知,提高前端网页性能的一个很重要的一点就是减少HTTP请求。所以尽最大可能的缓存资源,可以显著提高网站的加载速度。浏览器缓存主要有两个概念,强缓存与协商缓存,强缓存通俗理解就是,我第一次打开一个网页,向服务器发送了一个HTTP请求,服务器把资源给我的同时,通过一些响应头(如Expries和Cache-Control:max-age=36000)告诉我强制把资源缓存到本地,所以下一次直接在本地读取资源,而不是再向服务器端发送请求!协商缓存,是用户在请求资源后,浏览器返回资源,同时会返回一些与这个资源相关的信息,如Last-Modified和ETag响应头,下次用户再请求资源的时候,会与服务器进行协商,判断资源是否更新,如果更新则重新从服务端拉取数据,否则直接采用浏览器缓存的资源!

Expries与Cache-Control(强缓存)

在HTTP1.0时代,控制浏览器缓存主要有两种方式,expries和pargam。 1.expries是用来设置过期时间,可以控制文件缓存的失效日期,设置为0效果等于不缓存,而且expries设置的失效时间是一个固定的时间,不是相对的时间。比如到哪年哪月失效。注意:expries设置的时间是服务器端的时间,所以如果客户端的时间与服务器端的时间不一致,那么可能这个expries这个属性就失效了。因此,expries有时候并不是很准确,HTTP1.1推出了Cache-Control来解决时间误差问题,所以现在之所以还在使用expries这个属性主要就是为了向下兼容。 2.pargam是设置禁用缓存,比如设置Pargam: no-cache,则不允许缓存。这个属性的优先级比较高,如果同时存在expries,则expries设置的属性失效,浏览器不会缓存资源! 3.由于expries是以服务器端的时间为准,所以HTTP1.1引入了Cache-Control,Cache-Control与expries的作用一致,都是设置资源的过期时间,但是Cache-Control设置的是一个相对时间,比如Cache-Control:max-age=3600,所以不会存在服务器与客户端时间不一致导致缓存失败的问题。 如图 tupian 很多时候expries和Cache-Control是同时存在的,既然他俩的作用是一样的,那肯定会有一个优先级,根据规范,如果他俩同时存在,则Cache-Control的优先级更高!以Cache-Control设置的为准!也就是说优先级从高到低分别是 Pragma -> Cache-Control -> Expires 。如果有缓存,且缓存没有过期,则直接从缓存中获取资源,不会向服务器发送请求,并返回状态码200 (from cache)。

Cache-Control可在请求头中使用的参考属性如下:

Item Value 含义
Cache-Control: max-age = (seconds) 缓存的最大有效期,比如设置max-age= 3600,则代表资源缓存一个小时!一小时后失效
Cache-Control: max-stale[(seconds)] 表示客户端愿意接受一个过期的资源,如果设定了max-stale的值,则代表客户端可以接受一个过期资源,但是过期超出的时间应小于等于max-stale的值
Cache-Control: min-fresh = (seconds) 表示客户端希望在指定的时间内获取最新的响应。
Cache-control: no-cache 顾名思义,不会缓存
Cache-control: no-store 缓存不应存储有关客户端请求或服务器响应的任何内容。
Cache-control: no-transform 资源不要进行任何格式上的转换
Cache-control: only-if-cached 如果缓存存在,只使用缓存,无论原始服务器数据是否有更新。

Cache-Control可在响应求头中使用的参考属性如下:

Item Value 含义
Cache-Control: max-age = (seconds) 同请求头
Cache-Control: s-maxage=(seconds) 覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。
Cache-Control: public 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
Cache-Control: private 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。
Cache-control: no-cache 同请求头
Cache-control: no-store 同请求头
Cache-control: no-transform 同请求头
Cache-control: must-revalidate 缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。
Cache-Control: proxy-revalidate 与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。然后下次在发送这个

Last-Modified与If-Modified-Since(协商缓存)

当我们打开一个网页后,可以看一下他的响应头。可以看到这个Last-Modified属性,代表这次资源最后一次更新是2017年6月4号,然后状态码是200 OK。 Markdown 然后我们F5刷新一下这个网页。可以看到,我们的请求头中多了If-Modified-Since这个字段,这个字段是为了对比服务器端的资源是否发生了改变,如果两次时间相等,则说明资源没有发生变化,代表可以直接使用缓存的资源,那么浏览器就不需要重新下载资源,状态码返回304 Not Modified. Markdown 现在我们设想一下这个问题,由于之前的字段设置的时间单位最小只能精确到秒,如果服务器端的资源在1s内发生了多次修改怎么办?显然这个字段还存在问题。

ETag与If-None-Match(协商缓存)

还是我们刚才的网页,我们可以看到,第一次请求这个资源的时候,响应头里不仅有Last-Modified,而且还有一个ETag响应头,而且是一个字符串,这个ETag,是服务器端对该资源的一个标识符,所以下一次请求的时候,请求头中就会有If-None-Match,向服务器端进行匹配,如果ETag与If-None-Match的字符串一致,则代表资源没有被修改。可以直接从缓存中获取! Markdown 注意,If-None-Match与If-Modified-Since同时存在的时候,以If-None-Match为准,因为If-None-Match的精确度更高!

用户行为控制缓存

可以参考网上的这张图 Markdown

总结

整个浏览器请求与缓存相关的流程可以参考这两张图 浏览器第一次请求: Markdown 浏览器后续请求: Markdown 参考资料 1.MDN 2.浏览器缓存机制浅析 3.浏览器缓存

yleo77 commented 6 years ago
  1. 为什么 If-None-Match 的精确度更高,ETAG 的值是怎么来的,你没有提到。
  2. 作为扩充,这个 topic 有机会可以再外延下,和缓存相关的也有一些知识点,比如 localStorage 等,或者常见的一些 Method 浏览器是怎么控制缓存的。

格式上的一个建议,中英文之间可以用空格隔开,如果英文指代为一些关键字,可以加粗标识。