为什么要设置缓存
前端代码打包之后的生成的静态资源发布到静态服务器上,做运维配置,响应浏览器请求。静态服务器和浏览器配合进行缓存直接影响到网站性能和用户体验的,gzip和设置缓存是必不可少的。
只有get请求会被缓存,post请求不会
缓存的好处不言而喻,但是需要注意-资源如果有更改但是客户端不及时更新会造成用户获取信息滞后,所以掌握缓存的原理,更加合理的配置缓存非常重要
- 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
- 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
HTTP强缓存与协商缓存
查看该篇文章的网络请求
强缓存-服务器强制
当浏览器去请求某个文件的时候,服务端就在respone header里面对该文件做了缓存配置,由服务端控制缓存的时间、缓存类型
Expires 是http1.0的产物,Cache-Control是http1.1的产物,两者同时存在的话,Cache-Control优先级高于Expires
cache-control包括的常见设置有max-age public private no-cache no-store等
- max-age=xxx 客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存
- immutable 即使用户刷新页面,浏览器也不会发起请求,会直接从本地磁盘或者内存中读取缓存并返回200状态
- public 客户端和代理服务器都可以缓存该资源
- private 只让客户端可以缓存该资源;代理服务器不缓存
- no-cache 跳过设置强缓存,但是不妨碍协商缓存。一般,只有在强缓存失效才开始协商缓存的。
- no-store 客户端、代理服务器都不缓存,也就没有所谓的强缓存、协商缓存。
综上:
- 强缓存:给资源设置个过期时间,客户端每次请求资源时都会看是否过期;只有在过期才会去询问服务器。
- 当客户端请求该资源时发现其过期了,就会去请求服务器了,而这时候去请求服务器的这过程就可以设置协商缓存。
协商缓存
协商缓存就是需要客户端和服务器两端进行交互的。
response header:
- etag:文件hash,标识文件状态,改动文件了就变了
用webpack打包的时候,每个资源都会有。如: app.js打包后变为 app.c20abbde.js
- last-modified:文件的修改时间,精确到秒
- 每次请求返回的每个文件携带 response header 中的 etag和 last-modified,下次请求时浏览器在 request header 就携带信息if-none-matched、if-modified-since。
- 服务端把你带过来的标识进行对比,如果更改就返回200状态码、新资源,反之返回304状态码表示资源文件没有变化,可以直接使用缓存
HTTP1.1中etag的出现,主要是为了解决几个last-modified比较难解决的问题:
- 文件的周期性更改,但是实质内容并不改变 ==管太宽==
- if-modified-since能检查到的粒度是有限的,只能达到秒级的。文件修改(实质内容)非常频繁,甚至在秒以下的时间修改==管太窄==
- 某些服务器不能精确的得到文件的最后修改时间。==管不到==
合理设置强缓存与协商缓存
一般需要缓存的资源有html页面和其他静态资源
- index.html文件一般采用协商缓存不设置强缓存:用户每次请求index.html不拿浏览器缓存,直接请求服务器,用户能及时访问到新资源,如果服务端返回304,再取缓存
- 其他资源文件采用强缓存为主,协商缓存为辅
html页面缓存的设置主要是在head标签中嵌入meta标签,设置页面的缓存方式,对页面上的资源无效。
<meta http-equiv="cache-control" content="no-cache">
// 其他主流浏览器识别的标签
<meta http-equiv="pragma" content="no-cache">
// 仅有IE浏览器才识别的标签,不一定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求
<meta http-equiv="expires" content="0">
// 仅有IE浏览器才识别的标签,该方式仅仅作为知会IE缓存时间的标记,你并不能在请求或响应报文中找到Expires字段
<meta http-equiv="Cache-Control" content="max-age=7200" />
// 其他主流浏览器识别的标签
<meta http-equiv="Expires" content="Mon, 20 Aug 2018 23:00:00 GMT" />
// 仅有IE浏览器才识别的标签
资源的缓存策略一般由浏览器机制和服务器配置共同决定。静态资源的缓存一般是在web服务器上配置的,常用的web服务器有:nginx、apache。
后端服务器如nodejs:
res.setHeader('max-age': '3600 public')
res.setHeader(etag: '5c20abbd-e2e8')
res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
HTML5 manifest属性配置离线应用
为html 元素新增manifest属性,在开发离线应用程序时候,使用其引入 .manifest文件来指定需要缓存的文件
<html manifest="demo.manifest">
CACHE MANIFEST
#我是注释:第一行必须是上面
#manifest文件有三部分,三种文件,对应每一行是文件的相对路径(原服务器上)、或者其他服务器上文件
#第一部分:文件将在首次下载后进行缓存
/theme.css
/logo.gif
/main.js
https://editor.csdn.net/md/xx.jpg
NETWORK:
#文件需要与服务器连接,且不会被缓存
login.asp
FALLBACK:
#规定当页面无法访问时的回退页面(如404页面)
/html5/ /404.html
服务器需要提供支持,配置HTML5离线应用环境:配置正确的 MIME-type,即 "text/cache-manifest",才能解析manifest 文件
例如tomcat服务器在conf/web.xml中添加:
<mime-mapping>
<extension>manifest</extension>
<mime-type>text/cache-manifest</mime-type>
</mime-mapping>
这种方式的缓存会保持至 :
- 用户清空浏览器缓存
- manifest 文件被修改(浏览器会自动检查文件来进行更新)
- 程序更新应用缓存
举个例子
- 输入网址http:// www.hh.fake/ blog System/index.html,请求访问
- C收到S返回index.html文件,开始解析并请求页面中资源,包括html、js、css图片以及页面manifest属性指向的index. manifest
- C收到S返回的资源后,根据manifest文件缓存所需资源。并动态检测保持更新。
- C再次url访问网站,优先使用本地缓存index.html,并向S请求manifest文件,检查是否更新。
本地缓存对象applicationCache维护着本地缓存的各种状态及事件。通过JavaScript灵活交互
浏览器缓存位置
浏览器缓存位置一般有四类: Service Worker-->Memory Cache-->Disk Cache-->Push Cache
Service Worker进行缓存控制
Service Worker 可以理解为一个介于客户端和服务器之间的一个代理服务器
- Service Worker是事件驱动的,具有生命周期,其生命周期与页面完全无关。
- 基于web worker,是一个浏览器中的进程而不是浏览器内核下的线程,独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程
- 是运行在浏览器后台的独立线程,独立于当前页面,并且不能直接参与DOM操作,但是可以通过postMessage与页面通信来实现页面交互。
- 综上Service Worker 适合被用与多个页面需要使用的复杂数据的计算。
- 在web worker的基础上增加了离线缓存的能力,并且可以让开发者自己控制管理缓存的内容以及版本,可以访问cache和indexDB(sw为完全异步,不支持同步API如同步的XHR和localStorage)。管理内容包括自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
因为 Service Worker 中涉及到请求拦截,所以传输协议必须使用 HTTPS 协议或者本地localhost来保障安全。所以务必在 HTTPS 下运行你的程序
应用:
- 后台消息传递
- 网络代理:可以拦截全站的请求,并作出相应的动作。e.g拦截客户端的请求、向客户端发送消息、向服务器发起请求等等
- 离线资源缓存
- 消息推送
// index.js
//register 方法接受两个参数,第一个是 service worker 文件的路径,请注意:这个文件路径是相对于 Origin ,而不是当前 JS 文件的目录的;第二个参数是 Serivce Worker 的配置项,可选填
if ('serviceWorker' in window.navigator) {
navigator.serviceWorker.register('./sw.js', { scope: './' })
.then(function (reg) {
console.log('success', reg);
navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage("this message is from page");
})
.catch(function (err) {
console.log('fail', err);
});
}
//注册完 Service Worker 之后,浏览器会为我们自动安装它
// sw.js监听install
// sw.js
this.addEventListener('install', function (event) {
console.log('Service Worker install');
event.waitUntil(
caches.open('sw_demo').then(function (cache) {
return cache.addAll([
'/style.css',
'/panda.jpg',
'./main.js'
])
}
));
});
开发者工具application中可以查看ws
ws支持事件 install、message、fetch、activate、sync、push
//ws可以监听自己的scope下的页面的 fetch 事件,scope内每当用户向服务器发起请求的时候这个事件就会被触发。
this.addEventListener('fetch', function (event) {
console.log(event.request.url);
event.respondWith(
caches.match(event.request).then(res => {
return res ||
fetch(event.request)
.then(responese => {
const responeseClone = responese.clone();
caches.open('sw_demo').then(cache => {
cache.put(event.request, responeseClone);
})
return responese;
})
.catch(err => {
console.log(err);
});
})
)
});
Service Worker 实现
- 首先需要注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,
- 在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存
- 没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。
- 注意:不管是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示从 Service Worker 中获取的内容。
Memory Cache 内存中的缓存
主要包含的是当前中页面中已经抓取到的资源:
- 页面上已经下载的样式、脚本、图片
- preloader相关指令( <linkrel="prefetch">)下载的资源等。preloader的相关指令 是页面优化的常见手段之一, 可以一边解析js/css文件,一边网络请求一个资源。
- 缓存持续性很短,会随着进程的释放而释放。 一旦关闭 Tab 页面,内存中的缓存也就被释放了。
- 当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存。
- 资源的匹配除了对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。
Disk Cache
- 在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。
- 它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。
- 并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次请求。
Push Cache
Push Cache(推送缓存)是 HTTP 2 中的内容,当以上三种缓存都没有命中时,它才会被使用。
- Push Cache 中的缓存只能被使用一次
- 只在会话(Session)中存在,一旦会话结束就被释放
- 缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
- 可以推送 no-cache 和 no-store 的资源
- 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要取决于浏览器的实现,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。
- 浏览器可以拒绝接受已经存在的资源推送
- 你可以给其他域名推送资源
用户行为匹配缓存
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。但是如果默认加上Cache-Control:max-age=0,即会走协商缓存。
- 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control:no-cache(为了兼容,还带了 Pragma:no-cache),服务器直接返回 200 和最新内容。
- 其他浏览器都有清除缓存,禁用缓存的做法
REST
RESTful API :定义了使用 HTTP的限制,描述一种设计良好的 Web 应用:该应用是可靠的,运行良好,可扩展,设计优雅,而且方便修改。可供不同的浏览器和移动设备使用
REST 使用 Web 地址(URI)访问资源
即统一资源标识符。可进一步划分为统一资源名(URN,代表资源的名字)和统一资源定位符(URL,代表资源的地址)。
- 大部分情况下,URL 应使用小写字母,使用连字符
下划线,结尾使用斜杠- 不鼓励使用文件后缀,但当 API 支持多种格式,比如 XML 和 JSON 时,可使用文件后缀。
- 使用“Accept”和“Content-Type”头来控制格式,是一种更好的替代方案。
使用动词(HTTP 请求)操作资源
使用 GET、POST、PUT 和 DELETE在数据库或对象关系管理(ORM)系统中传统的 CRUD (Create/Read/Update/Delete)操作对应。
REST 架构规定:访问或修改 Web 上的资源,和访问或修改数据库中的记录一样,需要使用 SQL 或其他查询的语言。
get请求 = header请求 + body{}消息体
get与post
- 区别于get、put、delete具有幂等性,post方法不满足幂等性,因为post用于创建资源,所对应的URI并非创建的资源本身,而是去执行创建动作的操作者。 比如:
POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI。
- GET把参数包含在URL中,POST通过request body传递参数。这取决于HTTP的规定和浏览器/服务器的限制。
- GET产生一个TCP数据包;POST产生两个TCP数据包。取决于浏览器自己的实现
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(被接受);
- 而对于POST,浏览器先发送header,服务器响应100(继续), 再发送data,服务器响应200(被接受)
- GET在浏览器回退时是无害的,而POST会再次提交请求。
- GET产生的URL地址可以被Bookmark,而POST不可以。
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET请求只能进行url编码,而POST支持多种编码方式。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- GET请求在URL中传送的参数是有长度限制的,而POST没有。
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中。
REST 要求无状态的设计
- 无状态:前后数据包的发送、传输、接收都是独立的,互不影响
- 无连接:通信双方不长久维持对方的任何信息
会话数据保存在客户端:客户端的每次请求必须要包含整个客户端状态。服务器端在不通过额外查询应用状态的情况下,响应需求。因为所有的数据都包含在了请求里,使得每次交互的意图理解起来变得容易。同时会话数据不在服务器端维护,容错性和可扩展性都得到提升,服务器资源不会浪费在存储上。
REST 要求给数据打标签
标明其是否可被缓存,使得客户端应用能重用缓存的响应,而不是此后发送同等的请求。和任何系统的缓存一样,这会极大提高客户端应用性能。
同时,为避免数据过期,采用合适的缓存失效策略带来了复杂性。以 Web 来说,缓存可以存在于客户端(浏览器里)、服务器端,或者位于二者之间(网关或代理服务器)。
统一接口
规定了所有 RESTful 应用的通用资源定位方式。每一个 REST 应用都共享一种通用架构
报文头部信息
通用首部字段
- Date :创建报文时间
- Connection:连接的管理
- Cache-Control:缓存的控制
- Transfer-Encoding:报文主体的传输编码方式
请求首部字段
- Host:请求的资源所在的服务器,可用来支持一个web server配置多个虚拟服务器站点
- Accept:可处理的媒体类型
- Accept-Charset:可接受的字符集
- Accept-Encoding:可接受的内容编码
- Accept-Language:可接受的自然语言
响应首部字段
- Accept-Ranges:可接受的字节范围
- Location:令客户端重新定向到的URL
- Server:HTTP服务器的安装信息
实体首部字段
- Allow:资源可支持的HTTP方法
- Content-Type:实体主体的类型
- Content-Encoding:实体主体使用的编码方式
- Content-Language:实体主体的自然语言
- Content-Length:实体主体的字节数
- Content-Range:实体主体的位置范围,一般用于发出部分请求时使用
标准 HTTP 状态码
-
1xx:信息响应类,表示接收到请求并且继续处理
-
2xx:处理成功响应类,表示动作被成功接收、理解和接受
-
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
-
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
-
5xx:服务器端错误,服务器不能正确执行一个正确的请求
错误类 4xx、5xx
http-equiv属性
refresh 用于定时刷新网页或者跳转到其他网页
<meta http-equiv='Refresh' content='url'>
<meta http-equiv="cache-control" content="no-cache">
// 其他主流浏览器识别的标签
<meta http-equiv="pragma" content="no-cache">
// 仅有IE浏览器才识别的标签,不一定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求
<meta http-equiv="expires" content="0">
// 仅有IE浏览器才识别的标签,该方式仅仅作为知会IE缓存时间的标记,你并不能在请求或响应报文中找到Expires字段
<meta http-equiv="Cache-Control" content="max-age=7200" />
// 其他主流浏览器识别的标签
<meta http-equiv="Expires" content="Mon, 20 Aug 2018 23:00:00 GMT" />
// 仅有IE浏览器才识别的标签