【译】关于网站缓存的相关概念

228 阅读6分钟

有时候你会发现,当你第二次访问一个网站的时候,它貌似看起来不像希望的那样,样式丢了,看上去很挫。通常,这个问题的原因是没有很好的利用缓存策略,从而阻止你从服务器拉到最新的资源修改。通过这篇文章,我会告诉你设置缓存的正确姿势,帮助你每次部署好生产环境后,所有用户都能够看到最新的网站。

缓存是如何工作的?

浏览器在每次请求网页/资源的时候,都会通过读取本地缓存信息,来尽可能少地加载数据。这是唯一我们能够通过指令来告诉浏览器需要缓存住哪些资源以及缓存多久。

这些指令通常是你通过添加http响应头来告诉浏览器的。缓存处理所涉及到的最常用的指令是:“Cache-Conrol”,“Expires”,“Etag”“Last-Modified”

几乎所有网站服务器返回头都有默认的一套缓存设置,不过都不太明确。

如果没有缓存设置,一般浏览器每次请求都会走一遍服务器。这样就会增加网站加载时间,增加服务器的压力。

没有设置缓存的加载流程

response头没有缓存设置,那么浏览器将按照自身的缓存策略来行事。目前看来,chrome和safari会每次都走一遍服务器。不过这个行为也可能是不确定的,尤其在其它平台上。

为了明确定义缓存行为,让我们更加深入地学习一下缓存指令。

Etag (Entity tag)

Etag是缓存指令的其中一个。这个指令的主要目的是让浏览器不用下载整个文件,就能够知道这个文件是否有改动。服务器会计算每个文件的hash值,并且把这个值发送给客户端。下一次请求资源时,浏览器会发送http头并带上标记:If-None-Match: W/“1d2e7–1648e509289”。服务器会比较这个hash值和当前请求文件的hash值,如果不同,让客户端下载这个文件(资源有修改则返回200和数据)。否则,会告知客户端用缓存的版本(服务端返回304不返回数据)。


Etag 首次请求

 


Etag 二次请求

用了Etag,就会在服务端检测一下文件的hash值,之后浏览器再决定从缓存里拿还是从服务器重新加载。当资源没有修改时,只会使用大概80-100字节来验证一下,不管这个文件是10KB还是10MB。

Last Modified

另外一个HTTP头缓存指令是“Last Modified”。这个和Etag很像,不过浏览器的行为不太一样。服务器端对每一个文件都有一个最后修改的时间戳;当文件首次被加载后,(第二次加载时),客户端会去问服务端:“从我最后一次访问这个文件后,这个文件是否有改动?”。为了这个提问,浏览器会在header里发送 If-Modified-Since: Fri, 13 Jul 2018 10:49:23 GMT 。如果资源被改动过,浏览器就会下载新文件(200),否则就用缓存版本(304)。

事实上,浏览器内部有自己的缓存策略,它们自己会决定是否使用缓存资源还是下载一个新的。

` Last-Modified 是一个弱缓存指令,主要还是取决于浏览器自己的实现(美其名曰启发式策略。。。)


Last-Modified 首次请求

 


Last-Modified 二次请求(完美场景)

 


Last-Modified 二次请求(普遍场景)

结果是这东西靠不住,所以我倾向于不用这个指令。

Cache-Control max-age

这个指令目的是告诉浏览器在第一次加载后需要把这个资源在缓存里存多久,单位是秒,通常是:Cache-Control: max-age=31536000 。这个策略下,浏览器完全不会请求服务器并且很快的打开这个资源。但是我们怎么能够知道这个文件多久不会被修改呢?我们不能。。。

所以,为了强制浏览器能够下载到所需文件的最新版本,我们使用了一项技术手段,构建资源时使用Webpack或者Gulp。每个文件都会先编译,然后把文件内容的hash值追加到文件名后面,就像“app-72420c47cc.css”。即使再小的文件变化,也都会反应在hash值里,这就保证了它将被识别为不同。所以,下次部署后,你就能够得到一个新文件。这样就能够把(max-age=31536000)应用到我们所有的资源上(css,js,image);当我们更改了资源后,浏览器将加载一个新hash命名的新文件,然后它将会被缓存。(其实这里还涉及一步就是需要去修改一下引用资源的地址)

no-cache

上一个指令(Cache-Control max-age)棘手的地方在于,你不要忘了你的html文件;如果你把(max-age)设置到html文件上,那你将得不到新的资源地址,除非你强刷浏览器。。。

这里我就建议使用这个 Cache-Control: no-cache 来设置这些html文件。用了“no-cache”并不代表就没有缓存了,它只是简单告诉浏览器在从缓存里拿之前需要先通过服务器来验证一下资源。这就是为什么我们需要将它与Etag一起使用,因此浏览器将发送一个简单的请求并加载额外的80个字节以验证文件的状态。

以上原文

总结

  1. Cache-Control:no-cache 每次需要先问下服务器是否有文件变动(第3条)。注意:cache-control: no-cache作为请求头和响应头意义是不一样的。在请求头中发送时表示,(包括中间服务器)不要使用缓存,去源服务器请求资源。作为响应头表示每次都得去校验资源更新。
  2. 优先级:Cache-Control:max-age > Last-Modified > ETag
  3. 向服务器校验资源更新的指标有两个:ETag资源唯一标识,Last-Modified最后修改时刻。
  4. Cache-Control能够组合使用,比较常用的是Cache-Control:max-age=86400,no-cache,表示缓存1天,但是每次还是要向服务器验证资源(第3条)。
  5. IE8及以上能够支持http 1.1,以上说的指令都是需要支持http1.1。