浏览器缓存,想说爱你不容易

  今天小微开店宝在测试环境发布更新的时候,同事问:“为何我须要手动清理浏览器缓存才能看到变动?难道系统上线后也须要客户本身清理浏览器缓存吗!”看来,这个坑须要我来填了。前端

什么是浏览器缓存

浏览器缓存(Brower Caching)是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就能够直接从本地磁盘加载文档。web

浏览器缓存的优势有:面试

  1. 减小了冗余的数据传输,节省了网费
  2. 减小了服务器的负担,大大提高了网站的性能
  3. 加快了客户端加载网页的速度

在前端开发面试中,浏览器缓存是web性能优化面试题中很重要的一个知识点,从而说明浏览器缓存是提高web性能的一大利器,可是浏览器缓存若是使用不当,也会产生不少问题,正所谓是,想说爱你,并非很容易的事。因此,结合最近遇到的案例,本文对浏览器缓存相关的知识进行总结概括,但愿对读者有所帮助。浏览器

浏览器缓存的分类

浏览器缓存主要有两类:缓存协商完全缓存,也有称之为协商缓存强缓存缓存

浏览器在第一次请求发生后,再次请求时:性能优化

  1. 浏览器会先获取该资源缓存的header信息,根据其中的expirescahe-control判断是否命中强缓存,若命中则直接从缓存中获取资源,包括缓存的header信息,本次请求不会与服务器进行通讯;
  2. 若是没有命中强缓存,浏览器会发送请求到服务器,该请求会携带第一次请求返回的有关缓存的header字段信息(Last-Modified/IF-Modified-Since、Etag/IF-None-Match),由服务器根据请求中的相关header信息来对比结果是否命中协商缓存,若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,可是并不返回资源内容,它会告知浏览器能够直接从缓存获取;不然返回最新的资源内容

强缓存

强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。服务器

Expires
该字段是http1.0时的规范,它的值为一个绝对时间的GMT格式的时间字符串,好比Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间表明着这个资源的失效时间,在此时间以前,即命中缓存。这种方式有一个明显的缺点,因为失效时间是一个绝对时间,因此当服务器与客户端时间误差较大时,就会致使缓存混乱。性能

Cache-Control
Cache-Control是http1.1时出现的header信息,主要是利用该字段的max-age值来进行判断,它是一个相对时间,例如Cache-Control:max-age=3600,表明着资源的有效期是3600秒。cache-control除了该字段外,还有下面几个比较经常使用的设置值:测试

  • no-cache:不使用本地缓存。须要使用缓存协商,先与服务器确认返回的响应是否被更改,若是以前的响应中存在ETag,那么请求的时候会与服务端验证,若是资源未被更改,则能够避免从新下载。
  • no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
  • public:能够被全部的用户缓存,包括终端用户和CDN等中间代理服务器。
  • private:只能被终端用户的浏览器缓存,不容许CDN等中继缓存服务器对其缓存。

Cache-Control与Expires能够在服务端配置同时启用,同时启用的时候Cache-Control优先级高。优化

协商缓存

协商缓存就是由服务器来肯定缓存资源是否可用,因此客户端与服务器端要经过某种标识来进行通讯,从而让服务器判断请求资源是否能够缓存访问,这主要涉及到下面两组header字段,这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求则会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。

Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

当浏览器再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存以前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。

若是命中缓存,则返回304,而且不会返回资源内容,而且不会返回Last-Modify。

ETag/If-None-Match

与Last-Modify/If-Modify-Since不一样的是,Etag/If-None-Match返回的是一个校验码。ETag能够保证每个资源是惟一的,资源变化都会致使ETag变化。服务器根据浏览器上送的If-None-Match值来判断是否命中缓存。

与Last-Modified不同的是,当服务器返回304 Not Modified的响应时,因为ETag从新生成过,response header中还会把这个ETag返回,即便这个ETag跟以前的没有变化。

为何要有Etag

你可能会以为使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为何还须要Etag呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

  • 一些文件也许会周期性的更改,可是他的内容并不改变(仅仅改变的修改时间),这个时候咱们并不但愿客户端认为这个文件被修改了,而从新GET;
  • 某些文件修改很是频繁,好比在秒如下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改没法判断(或者说UNIX记录MTIME只能精确到秒);
  • 某些服务器不能精确的获得文件的最后修改时间。

Last-Modified与ETag是能够一块儿使用的,服务器会优先验证ETag,一致的状况下,才会继续比对Last-Modified,最后才决定是否返回304。

强缓存与协商缓存的区别能够用下表来表示:
 |获取资源形式|状态码|发送请求到服务器
------|------------|------|----------------
强缓存|从缓存取 |200(from cache)|否,直接从缓存取
协商缓存|从缓存取|304(Not Modified)|否,经过服务器来告知缓存是否可用

用户行为对缓存的影响

用户操做 Expires/Cache-Control Last-Modied/Etag
地址栏回车 有效 有效
页面连接跳转 有效 有效
新开窗口 有效 有效
前进回退 有效 有效
F5刷新 无效 有效
Ctrl+F5强制刷新 无效 无效

实际问题分析

如文章开头所属,代码更新到线上后用户浏览器不能自行更新,咱们不能要求客户在系统更新后都进行一次缓存清理的操做。

到底该如何解决呢?

在资源请求的URL中增长一个参数,好比:js/mian.js?ver=0.7.1。这个参数是一个版本号,每一次部署的时候变动一下,当这个参数变化的时候,强缓存都会失效并从新加载。这样一来,静态资源,部署之后就须要从新加载。这样就比较完美的解决了问题。

进一步思考

这样作是否是最完美的呢?很遗憾,不是。

至于有什么更好的方法,欢迎你们留言讨论。

相关文章
相关标签/搜索