应用场景
前端代码打包之后的生成的静态资源就要发布到静态服务器上,需要做运维配置。
gzip和设置缓存是必不可少的。这两项是最直接影响到网站性能和用户体验的。
只有get请求会被缓存,post请求不会
缓存可以减少了不必要的数据传输,节省带宽;减少服务器的负担,提升网站性能;加快了客户端加载网页的速度
但是需要注意:
资源如果有更改但是客户端不及时更新会造成用户获取信息滞后
所以掌握缓存的原理,更加合理的配置缓存是非常重要滴。
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
HTTP强缓存与协商缓存
查看该篇文章的网络请求
强缓存
强制
当浏览器去请求某个文件的时候,服务端就在respone header里面对该文件做了缓存配置,由服务端控制缓存的时间、缓存类型。
即respone header 的cache-control,常见的设置是max-age public private no-cache no-store等
Expires 是http1.0的产物,Cache-Control是http1.1的产物,两者同时存在的话,Cache-Control优先级高于Expires
max-age=xxx
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200
有效期内客户刷新操作,就向服务器发http请求
- immutable 即使用户刷新页面,浏览器也不会发起请求,会直接从本地磁盘或者内存中读取缓存并返回200状态
- public 客户端和代理服务器都可以缓存该资源
- private 只让客户端可以缓存该资源;代理服务器不缓存
- no-cache 跳过设置强缓存,但是不妨碍协商缓存。一般,只有在强缓存失效才开始协商缓存的。
- no-store 客户端、代理服务器都不缓存,也就没有所谓的强缓存、协商缓存。
综上
强缓存就是给资源设置个过期时间,客户端每次请求资源时都会看是否过期;只有在过期才会去询问服务器。
当客户端请求该资源时发现其过期了,就会去请求服务器了,而这时候去请求服务器的这过程就可以设置协商缓存。
协商缓存
协商缓存就是需要客户端和服务器两端进行交互的。
response header里面的设置
etag:文件hash,标识文件状态,改动文件了就变了
用webpack打包的时候,每个资源都会有。如: app.js打包后变为 app.c20abbde.js,加个唯一hash,也是为了解决缓存问题。
last-modified:文件的修改时间,精确到秒
每次请求返回的每个文件携带 response header 中的 etag和 last-modified,下次请求时在 request header 就把信息携带上,服务端把你带过来的标识进行对比,如果更改就返回200状态码、新资源,反之返回304状态码–>客户端用缓存的老资源。
在客户端重新向服务端发起请求时的etag、last-modified,在request header中换了key名,if-none-matched、if-modified-since
HTTP1.1中etag的出现,主要是为了解决几个last-modified比较难解决的问题:
- 文件的周期性更改,但是实质内容并不改变 管太宽
- if-modified-since能检查到的粒度是有限的,只能达到秒级的。文件修改(实质内容)非常频繁,甚至在秒以下的时间修改管太窄
- 某些服务器不能精确的得到文件的最后修改时间。管不到
设置强缓存与协商缓存
一般需要缓存的资源有html页面和其他静态资源
html页面缓存的设置主要是在< head>标签中嵌入< meta>标签,这种方式只对页面有效,对页面上的资源无效。
index.html文件一般采用协商缓存不设置强缓存,用户每次请求index.html不拿浏览器缓存,直接请求服务器,用户能及时访问到新资源,如果服务端返回304,这时候再拿浏览器的缓存的index.html
其他资源采用强缓存 + 协商缓存。
<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
可以把 Service Worker 理解为一个介于客户端和服务器之间的一个代理服务器
基于web worker(独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程)
是运行在浏览器后台的独立线程,独立于当前页面,并且不能直接参与DOM操作,但是可以通过postMessage与页面通信来实现页面交互。
Service Worker是事件驱动的,具有生命周期。其生命周期与页面完全无关。
Service Worker 是一个浏览器中的进程而不是浏览器内核下的线程,因此在被注册安装之后,能够被在多个页面中使用,也不会因为页面的关闭而被销毁。因此,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
内存中的缓存
主要包含的是当前中页面中已经抓取到的资源,e.g:页面上已经下载的样式、脚本、图片、preloader相关指令( <linkrel=“prefetch”>)下载的资源等。(preloader的相关指令 是页面优化的常见手段之一, 可以一边解析js/css文件,一边网络请求一个资源。)
缓存持续性很短,会随着进程的释放而释放。 一旦关闭 Tab 页面,内存中的缓存也就被释放了。
当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存。
需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配除了对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 和最新内容。
其他浏览器都有清除缓存,禁用缓存的做法