一、若是请求命中本地缓存则从本地缓存中获取一个对应资源的"copy";
二、检查这个"copy"是否fresh,是则直接返回,不然继续向服务器转发请求。
三、服务器接收到请求,而后判断资源是否变动,是则返回新内容,不然返回304,未变动。
四、客户端更新本地缓存。 css
no-cache:强制客户端跳过步骤2,直接向服务器发送请求。也就是说每次请求都必须向服务器发送。
must-revalidate:意味着某个资源在本地已缓存时长短于 max-age
指定时长时,能够直接使用,不然就要发起验证。
no-store:缓存将不存储response,包括header和body。测试结果代表,除每次请求都必发送到服务器外,响应代码均是200,且request并无发送"If-Modified-Since"和"If-None-Match"头,这意味着缓存的确没有存储response。
以上三者都是要求客户端每次请求都必须到服务器进行revalidate,此功能还能够经过max-age=0实现git
使用缓存会带来巨大的性能提高,还能节省带宽、减小服务端开销,但不少网站对缓存只知其一;不知其二,让相互依赖的资源出现竞态条件,从而没法同步更新。github
使用缓存的最佳实践大致上能够概括为这两种模式:django
Cache-Control: max-age=31536000gulp
max-age
指定时间内,缓存副本能够直接使用,不须要与服务端协商;页面:嘿,我须要 "/script-v1.js"、"/styles-v1.css" 和 "/cats-v1.jpg"。(10:24)浏览器
缓存:我这儿都没有。 服务端
,你有么?(10:24)缓存
服务端:固然,给你。对了 缓存
,把这些资源存起来,一年以内直接用吧。(10:25)服务器
缓存:多谢!(10:25)网络
页面:赞哦!(10:25)框架
页面:嘿,此次我要 "/script- v2 .js"、"/styles- v2 .css" 和 "/cats-v1.jpg"。(08:14)
缓存:我只有其中一个,先拿去用,剩下的我没有。 服务端
,看看?
服务端:好,给你新的 CSS 和 JS 文件。对了 缓存
,这些你也留着用一年吧。(08:15)
缓存:超赞!(08:15)
页面:多谢!(08:15)
缓存:嗯,"/script-v1.js" 和 "/styles-v1.css" 有一阵子没被用到。我决定删掉它们。(12:32)
在这种模式中,绝对不会修改某个 URL 的内容,只会改变 URL 自己:
<script src="/script-f93bca2c.js"> <link rel="stylesheet" href="/styles-a837cb1e.css"> <img src="/cats-0e9a2ef4.jpg" alt="…">
上面每一个 URL 都包含了与文件内容同步修改的部分。这部分能够是版本号、修改时间,或者是文件内容的 MD5 —— 个人博客就是这么干的。
大部分服务端框架都有对应工具来轻松完成这项工做(我在用 Django 的 ManifestStaticFilesStorage ),还有一些很轻量的 Node.js 库能够实现一样功能,例如 gulp-rev 。
可是,文章或博客详情这类 HTML 页面不适用于这种模式。这些页面 URL 不能包含版本号,页面内容还必须能修改。尤为是发现有拼写错误或语法错误后,须要快速频繁地更新。
Cache-Control: no-cache
页面:嘿,我须要 "/about/" 和 "/sw.js" 两份资源。(11:32)
缓存:我搞不定。 服务端
,看看?(11:32)
服务端:哈,我有,给你。 缓存
,你能够本身存一份,但用以前要先问我哈。(11:33)
缓存:明白!(11:33)
页面:多谢!(11:33)
页面:我又想要 "/about/" 和 "/sw.js"。(9:46)
缓存:等一下。 服务端
,我能直接用我本地的副本吗?在我这儿,"/about/" 最后修改于周一,"/sw.js" 最后修改于昨天。(9:46)
服务端:"/sw.js" 在那以后就没改过呢。(9:47)
缓存:赞! 页面
,给你 "/sw.js"。(9:47)
服务端:但 "/about/" 改过,我给你最新的版本。 缓存
,跟以前同样,你能够本身存一份,但用以前要先问我~(9:47)
缓存:了解!(9:47)
页面:赞噢!(9:47)
注: no-cache
并非说「不缓存」,它意味着使用缓存前必须检查(或者说 验证
)这个资源在服务端是否有更新。 no-store
用来告知浏览器彻底不要缓存这个资源。相似的, must-revalidate
并非说「每次都要验证」,它意味着某个资源在本地已缓存时长短于 max-age
指定时长时,能够直接使用,不然就要发起验证。好,这下明白了。
这种模式下,也能够给资源响应加上 ETag
(资源的版本 ID)、 Last-Modified
时间这两个头部。下次客户端请求这些资源时,会经过 If-None-Match
或 If-Modified-Since
这两个请求头带上以前的值,这样服务端就能够返回「直接用你以前缓存的版本吧,它们是最新的」,换成行话就是「HTTP 304」。
若是服务端没办法发送 ETag
/ Last-Modified
头部,那每次都须要发送完整的响应内容。
这种模式下,每次都会产生网络请求,因此它没有能节省网络请求的模式一好。
模式一被基础设施影响,模式二被网络请求影响,都是常见的事儿。因此又有了中间方案:给可变内容加上短一点的 max-age
。这个折中方案太太太糟糕了。
不幸的是这种作法并不罕见,Github pages 当前就是这样。
假设这样三个资源:
/article/
/styles.css
/script.js
都有这样的响应头:
Cache-Control: must-revalidate, max-age=600
If-Modified-Since
和 If-None-Match
请求头;页面:嘿,我须要 "/article/"、"/script.js" 和 "/styles.css"。(10:21)
缓存:我这里没有, 服务端
?(10:21)
服务端:没问题,给你。对了 缓存
,这些资源你能够存 10 分钟。(10:22)
缓存:明白!(10:22)
页面:多谢!(10:22)
页面:嘿,我又想要 "/article/"、"/script.js" 和 "/styles.css"。(10:28)
缓存:天哪,真抱歉,我把 "/styles.css" 给弄丢了,其它的都有,先给你。 服务端
,你能再把 "/style.css" 发给我吗?(10:28)
服务端:固然能够,实际上它在你上次请求以后发生了改变。一样,你又能够把它缓存 10 分钟。(10:29)
缓存:没问题。(10:29)
页面:多谢!等等!完全挂了!!发生什么啦?(10:29)
这种场景在测试环境能够构造出来,但在真实环境中难复现,也难追查。在上述例子中,实际上服务端同时更新了 HTML、CSS 和 JS,可是页面最终从缓存中拿到旧的 HTML 和 JS,并从服务端拿到最新的 CSS。版本不匹配致使功能异常。
一般,当咱们对 HTML 改动很大时,极可能 CSS 须要为新结构做出调整,JS 也须要配合 CSS 和 HTML 改动而进行相应修改。这些资源相互依赖,但没法经过缓存头反映出来。最终页面可能会拿到一部分新资源,一部分旧资源。
max-age
是响应时间的相对值,某个页面上的全部资源请求,会被设置在大体相同的时间后失效,但仍有小几率出现竞争。若是你有一些不包含 JS、或包含不一样 CSS 的页面,过时时间能够不一样步。更为糟糕的是,浏览器一直都在淘汰缓存的资源,它不可能知道某些 HTML、CSS 和 JS 相互有依赖,因此会出现部分淘汰的状况。综上所述,最终页面拿到版本不匹配的资源并不是不可能发生。
对于用来来讲,这会破坏页面布局和/或功能,从小问题到大事故都有可能发生。
谢天谢地,咱们有个解决方案。。。
刷新页面,会让浏览器向服务端发起验证,忽略 max-age
。因此若是用户对 max-age
的这个问题颇有经验的话,点击刷新按钮就能解决一切问题。固然,要求用户这样作会下降用户对你的信任,会让用户以为你的网站很不稳定。
给常常改变的内容设置 max-age
一般是错误的选择,但也不全是。如今你看到的这个页面(译者注:指原文页面)就设置了三分钟的 max-age
。在这里竞态条件不是问题,由于这个页面不依赖任何使用一样缓存模式的资源(个人 CSS、JS 及图片都属于模式一:不变内容),也不被其它使用一样缓存模式的页面所依赖。
这种模式意味着,若是我足够幸运写了一篇大受欢迎的文章,个人 CDN(Cloudflare)会帮个人服务器抗住流量,只要我能接受修改文章要等三分钟才能被用户看到,我确实能够接受。
这种模式用起来也没那么容易。若是我给一篇文章增长了一段内容,再在另一篇文章中指向它,我就在页面之间建立了会引入竞争的依赖关系。用户可能点击连接后看到的是不包含新增内容的缓存副本。要避免这种问题发生,我必须在更新文章后,去 Cloudflare 的控制台刷新这篇文章的缓存,等三分钟再在其它文章加上指向它的连接。是的,你必须很是当心地使用这种模式。
只要用法恰当,缓存能极大的提高性能、节省带宽。让不变内容能够轻松改变 URL,让可变内容走服务端验证。若是你很勇敢,当你能确认你的内容既不依赖别人也不被别人依赖时,才针对可变内容使用 max-age
,由于它可能没法同步更新。
从新载入:不额外添加cache-control
f5或刷新按钮:cache-control:max-age=0 发送确认请求 检查资源是否修改 ctrl+f5: cache-control:no-cache 并清空缓存 pragma:no-cache