1.按缓存位置分类
按缓存位置分类,可以分为:Memory Cache,Disk Cache, Service Worker ,以及网络请求
1.1Memory Cache
memory cache 叫内存缓存,几乎所有的网络请求资源都会被浏览器自动加入到 memory cache 中。而如果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前,排在前面的缓存就已经失效了。
预加载(preload)请求过来的资源就会被放入 memory cache 中,供之后使用。
memory cache 机制保证了一个页面中如果有两个相同的请求 (例如两个 src 相同的 ,两个 href 相同的 )都实际只会被请求最多一次,避免浪费。
不过在匹配缓存时,除了匹配完全相同的 URL 之外,还会比对他们的类型,CORS 中的域名规则等。
因此一个作为脚本 (script) 类型被缓存的资源是不能用在图片 (image) 类型的请求中的,即便他们 src 相等。
如果想让 memory cache 也不存储,那就需要在 Http 请求头设置 no-store。
1.2Disk Cache (http缓存)
disk cache 也叫 HTTP cache,是磁盘缓存。它允许相同的资源在跨会话,甚至跨站点的情况下使用,例如两个站点都使用了同一张图片。
disk cache 会严格根据 HTTP 头部中的各类字段来进行缓存。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。
凡是持久性存储都会面临容量增长的问题,disk cache 也不例外。浏览器会根据自身算法自动清理“最老的”或者“最可能过时的”资源。
1.3Service Worker
上述的缓存策略以及缓存/读取/失效的动作都是由浏览器内部判断和进行的,我们只能设置响应头的某些字段来告诉浏览器,而不能自己操作。
但 Service Worker 的出现,给予了我们另外一种更加灵活,更加直接的操作方式。我们可以选择缓存哪些文件,路由匹配规则,缓存匹配并返回。
Service Worker 借鉴了 Web Worker的 思路,即让 JS 运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问
DOM。虽然如此,但它仍然能帮助我们完成很多有用的功能,比如离线缓存、消息推送和网络代理等功能。其中的离线缓存就是 Service Worker Cache。
Service Worker 能够操作的缓存是有别于浏览器内部的 memory cache 或者 disk cache 的。我们可以在 Chrome 开发者工具,Application -> Cache Storage 找到缓存的位置。这个缓存是永久性的,即关闭 TAB 或者浏览器,下次打开依然还在。有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制,被浏览器全部清空。
如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。
1.4查找资源的优先级
ServiceWorker ----> memory cache ---------> disk cache ---------> 发送请求
1.5如何查看数据是否来自缓存?
使用google浏览器
2.重点http缓存
浏览器有个缓存数据库,这个数据库是为http缓存服务的,当请求的数据被缓存过,那么请求的数据直接从缓存数据库拿取。
http缓存分为强缓存与协商缓存
2.1强缓存
浏览器第一次请求的数据被服务器设置成有强缓存时间的数据,那么浏览器第二次请求的时候,如果时间未过期,那么请求将不会发送到服务器,数据直接从浏览器缓存数据库获取。(从google浏览器可以看到是from disk cache)
2.1.1设置的方法:
响应头中设置 Expires
Expires: Thu, 10 Nov 2017 08:45:11 GMT // gmt 绝对时间
如果跟 cache-control 的 max-age 同时存在,Expires 会被 max-age 覆盖 。 http1.1已经把它抛弃了,基本没用了。
响应头中设置 Cache-control
Cache-Control: max-age=2592000
表示缓存内容将在t秒后失效(以秒为单位),在该时间内,客户端不需要向服务器发送请求。
Cache-Control: no-cache
可以在本地和代理服务器缓存,但是这个缓存需要服务器验证才可以使用,即直接进入协商缓存阶段。
Cache-Control: no-store
真正意义上的“不要缓存”,不进行任何形式的缓存,每次都从服务器获取
Cache-Control: private
客户端可以缓存,代理服务器不能缓存。例如,用户的浏览器可以缓存包含用户私人信息的HTML网页,但CDN不能缓存
Cache-Control: public
客户端和代理服务器都可以缓存
Cache-Control: must-revalidate
must-revalidate告诉缓存,在事先没有跟原始服务器进行再验证的情况下,不能提供这个对象的陈旧副本,缓存仍然可以随意提供新鲜的副本。如果在缓存进行must-revalidate新鲜度检查时,原始服务器不可用,缓存就必须返回一条504错误。
Cache-Control: s-maxage
s-maxage是针对代理服务器的缓存时间
2.1.2实践代码
const express = require('express');const app = express();const port = 8080;const fs = require('fs');const path = require('path');const moment = require('moment');app.get('/',(req,res) => {res.send(`<!DOCTYPE html><html lang="en"><head><title>Document</title></head><body>Http Cache Demo<input type='button' onclick='getData()' value='获取数据'><script src="/demo.js"></script><script >function getData(){const xhr = new XMLHttpRequest()xhr.onreadystatechange = () => {if (xhr.readystate == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {alert(xhr.responseText)} else {alert("Request was unsuccessful: " + xhr.status)}}}xhr.open("get", "/demo1.js", true)xhr.send(null)}</script></body></html>`)})function getGLNZ(){ return moment().utc().add(2,'m').format('ddd, DD MMM YYYY HH:mm:ss')+' GMT';}app.get('/demo1.js',(req, res)=>{ let jsPath = path.resolve(__dirname,'./static/js/demo1.js'); let cont = fs.readFileSync(jsPath); res.setHeader('Cache-Control', 'public') res.setHeader('Cache-Control', 'max-age=31536000') console.log(123) res.end(cont)})app.listen(port,()=>{ console.log(`listen on ${port}`)})
结果:
2.2协商缓存
当强缓存的时间过期了,但是服务器的资源并没有发生改变,那么服务器就需要发响应数据的内容给浏览器了,只需要发状态码为304的响应头告诉浏览器继续去缓存数据库拿数据。
2.2.1设置的方法:
方法1: 服务器 Last-Modified 浏览器 If-Modified-Since
具体流程如下:客户端第一次向服务器发起请求,服务器将最后的修改日期(Last-Modified)附加到所提供的资源上去当再一次请求资源时,如果没有命中强缓存,在执行再验证时,会包含一个If-Modifed-Since首部,值为资源的 Last-Modified 日期,询问服务器该资源自从这个 Last-Modified 日期之后有没有被修改过。如果内容被修改了,服务器回送新的资源,返回200状态码和最新的修改日期如果内容没有被修改,会返回一个304 Not Modified响应
方法2:服务器ETag 浏览器If-None-Match
具体流程和 If-Modified-Since 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器对比已缓存标签与服务器文档中的标签是否有所不同,相同返回 304, 不相同返回新资源和 200。
以上默认设置都是基于express框架
2.2.2实际代码
const express = require('express');const app = express();const port = 8080;const fs = require('fs');const path = require('path');const moment = require('moment');app.get('/',(req,res) => {res.send(`<!DOCTYPE html><html lang="en"><head><title>Document</title></head><body>Http Cache Demo<input type='button' onclick='getData()' value='获取数据'><script src="/demo.js"></script><script >function getData(){const xhr = new XMLHttpRequest()xhr.onreadystatechange = () => {if (xhr.readystate == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {alert(xhr.responseText)} else {alert("Request was unsuccessful: " + xhr.status)}}}xhr.open("get", "/demo2.js", true)xhr.send(null)}</script></body></html>`)})function getGLNZ(){return moment().utc().add(2,'m').format('ddd, DD MMM YYYY HH:mm:ss')+' GMT';}app.get('/demo2.js',(req, res)=>{ let jsPath = path.resolve(__dirname,'./static/js/demo1.js'); let cont = fs.readFileSync(jsPath); let status = fs.statSync(jsPath) let lastModified = status.mtime.toUTCString() if(lastModified === req.headers['if-modified-since']){ res.writeHead(304, 'Not Modified') res.end() } else { res.setHeader('Cache-Control', 'public') res.setHeader('Cache-Control', 'max-age=5') res.setHeader('Last-Modified', lastModified) res.writeHead(200, 'OK') res.end(cont)}})app.listen(port,()=>{console.log(`listen on ${port}`)})
3.更新和废弃缓存的方法
将资源文件更改成hash名称,迫使请求路径发生变化,从而开始下载新的资源。
4.缓存应用
不常变化的资源
Cache-Control: max-age=31536000
经常变化的资源
Cache-Control: no-cache
5.参考资料来源
blog.csdn.net/guduyibeizi…
juejin.im/post/684490…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!