1. 简介

碰到的缓存问题…

  • 每次都加载同样的静态文件?
  • 协商缓存还是要和服务器通信,怎样可以省掉这个请求?
  • 这些数据、文件的有效时间要怎么定,会不会太容易失效?
  • 重复的无效请求太多?数据需要缓存起来,应该放在那里?
  • 如何更新缓存,更新的依据是什么?
  • 什么是Memory Cache, Disk Cache, Push Cache

这里罗列了缓存的所有分类..

2. HTTP header缓存机制

2.1 状态码

浏览器根据第一次请求资源时返回的 响应头 来确定,一个资源该不该被缓存。如果这是个可以被缓存的资源,浏览器会把它存到内存(memory cache)或者磁盘(disk cache)中,同时把缓存标识记录下来。

第二次请求资源时,会返回这些状态码

状态码
200 强缓存 Expires/Cache-Control失效时,并且协议缓存过期,返回新的资源文件
200(from cache) 强缓存 Expires/Cache-Control未过期,浏览器从本地获取资源成功。
304(Not Modified) 协商缓存Last-modified/Etag没有过期时,服务端返回状态码304

2.1 强缓存

不会向服务器发送请求,直接从缓存中读取资源。强缓存通过Expires和Cache-Control两种响应头实现。

1. Expires
Expires是HTTP 1.0提出的一个表示资源过期时间的字段,描述一个绝对时间,由服务器返回。Expires受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

1
Expires: Wed, 11 May 2018 07:20:00 GMT

2. Cache-Control
出现于HTTP 1.1,优先级高于Expires, 表示的是相对时间
Cache-Control: no-store 浏览器和缓存服务器,不缓存数据到本地磁盘中
Cache-Control: no-cache 浏览器和缓存服务器不应该缓存页面信息
Cache-Control: public 可以被所有用户缓存,包括终端和CDN等中间代理服务器
Cache-Control: private 只能被终端浏览器缓存,不允许中继缓存服务器进行缓存
Cache-Control: max-age=30缓存30秒后就过期,需要重新请求
Cache-Control: s-maxage=30覆盖max-age,作用一样,只在代理服务器生效

2.2 协商缓存

强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存。协商缓存利用Last-Modified, If-Modified-SinceETag, If-None-Match这两对标识来管理。

1. Last-Modified, If-Modified-Since
表示本地文件最后修改日期。
浏览器会在request header加上If-Modified-Since(上次响应头中的Last-Modified),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。但是如果在本地打开缓存文件,就会造成Last-Modified被修改,所以在HTTP / 1.1出现了ETag。

2. ETag, If-None-Match
ETag像一个指纹,资源变化都会导致ETag变化,它可以保证每一个资源是唯一的。
浏览器会在request header加上If-None-Match(上次相应头中的ETag)发送给服务器,询问该资源的ETag是否有更新,有变动就会发送新的资源回来。

ETag的优先级比Last-Modified更高,优先使用ETag有下面几种情况考虑:

  • 一些文件也许会周期性的更改,但它的内容并不改变(仅仅改变的是修改时间)
  • 某些文件修改非常频繁,If-Modified-Since能检查到的粒度是s级,对于秒以下的修改则无法判断
  • 某些服务器不能精确的得到文件的最后修改时间

2.3 缓存策略

  • 配置超长时间的本地缓存
  • 采用内容摘要作为缓存更新依据 —— 精确控制缓存
  • 静态资源部署CDN
  • 静态资源文件通过Service Worker进行缓存控制和离线化加载
  • 更新资源实现非覆盖式发布

具体操作

  • 对于不需要缓存的资源,使用Cache-control: no-store
  • 对于频繁变动的资源(比如经常需要刷新的首页,资讯论坛新闻类),可以使用Cache-control: no-cache并配合ETag,表示该资源已被缓存,但是每次都会发送请求询问资源是否需要更新
  • 对于代码文件,通常使用Cache-control: max-age=31536000强缓存,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件

3. 数据存储

除了请求缓存,我们还会把存储从服务器获取的数据,也是页面需要频繁用到的数据
cookie:4K,可以手动设置失效期
localStorage:5M,除非手动清除,否则一直存在
sessionStorage:5M,不可以跨标签访问,页面关闭就清理
indexedDB :浏览器端数据库,无限容量,除非手动清除,否则一直存在

共同点

  • 都是保存在浏览器端、且同源的

区别

  • cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
  • 存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  • 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
  • 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
  • web Storage支持事件通知机制,可以将数据更新的通知发送给监听者。接口使用更方便

4. 离线缓存PWA

使用Service Worker实现离线可用的应用,甚至在离线时,可以应用XHR数据,提高响应速度。
查看Demo

  • 在index.js中注册Service Worker(sw.js)
  • 在sw.js中列一个资源列表(离线时显示的页面),当Server Worker被激活时,将列表中的资源缓存进cache
  • 拦截浏览器请求:当浏览器请求各类静态资源(html/js/css/img)时, Service Worker拦截该请求,并查询当前cache。若存在cache则直接返回,先渲染数据。然后通过fetch方法向服务端发起请求。

参考资料