我的博客css
在平常开发中,cookie、Session/Local,对后两种运用的较少。
html
cookie是客户端的解决方案,最先是网景公司的前雇员Lou Montulli在1993年3月发明的。众所周知,HTTP是一个无状态的协议,客户端发起一次请求,服务器处理来自客户端的请求,而后给客户端回送一条响应。在客户端与服务器数据交换完毕后,服务器端和客户端的链接就会被关闭,Web服务器几乎没有什么信息能够判断是哪一个用户发送的请求,也没法记录来访用户的请求序列,每次交换数据都须要创建新的链接,后来有了用户,网站想要去了解用户的需求,可是根据当时场景显然没法知足业务需求,cookie便孕育而出,它能够弥补HTTP协议无状态的部分不足。前端
咱们先来看一下在平常操做中是怎么接收到这个cookie的。vue
开始以前,将本地cookie的值所有清除。nginx
输入'掘金',百度监听了focus
和input
事件,因此第一次触发了2次请求,奇怪的是第一次没有Set-cookie返回:git
第一次触发的是focus(),这时候Request Header请求头是没有任何Cookie,查询字段wd
没有参数,历史记录Hisdata读取的是LocalStorage里客户端的历史查询与时间戳。看第二次请求github
服务器传回了7条Set-Cooie,客户端拿到数据后,进行储存web
这时候能够看到Cookie
中有服务器返回的全部cookie以及相对应的值和其余配置;chrome
每次客户端第一次请求,服务器接收到请求后为该客户端设置cookie
,服务器端向客户端发送Cookie
是经过HTTP
响应报文实现的,在Set-Cookie
中设置须要向客户端发送的cookie
,浏览器接收到响应缓存cookie
到内存或者硬盘(视浏览器机制),以后每次发送请求都会携带上cookie
:数据库
cookie格式以下:
接收到的cookie格式
Set-cookie: name=value [; expires=date] [; path=path] [; domain=domain] [;secure=secure]
发送的cookie格式
Cookie: name1=value1 [; name2=value2]
咱们能够手动设置一下cookie的返回,
var http = require('http');
var fs = require('fs');
http.createServer(function(request, responed) {
responed.setHeader('status', '200 OK');
responed.setHeader('Set-Cookie', 'userStatus=true;domain=.juejin.com;path=/;max-age=1000');
responed.write('掘金');
responed.end();
}).listen(8888);
复制代码
而后启动服务器,访问的时候就能看到服务器返回
Set-Cookie:userStatus=true;domain=.juejin.com;path=/;max-age=1000 复制代码
在业务开发场景中,Cookie更多的是做为一种标识,用来记录用户的行为,而并不是用户的身份信息,根据用户登陆后生成特定Cookie,再次发起其余请求的时候,服务器可以识别用户是否可以继续这次操做,不能则相应操做。若是将用户的身份信息及其余重要信息放在cookie中是十分危险的,并且对于大型流量网站来讲,每个字节的消耗一年下来都是按几十TB算的,
以谷歌为例:google
的流量,占到整个互联网的40%
,2016
年全球网路流量达到1.3ZB
(1ZB
= 10^9TB
),那么google
在2016
年的流量就是1.3ZB
*40%
,若是google
每1MB
请求减小一个字节,每一年能够节省近500TB。
SessionStorage简称会话存储,和LocalStorage本地储存是前端如今最为普遍也是操做最简单的本地存储,都遵循同源策略(域名、协议、端口),存放空间很大,通常是5M,极大的解决了以前只能用Cookie来存储数据的容量小、存取不便、容易被清除的问题。这个功能为客户端提供了极大的灵活性。
并不是是只支持IE8以上,能够经过MDN官方的方法,变相的存储在Cookie内,不过这样的话有失Storage对象存在的意义了。
Session只做用于当前窗口,不能跨窗口读取其余窗口的SessionStorage数据库信息,浏览器每次新建、关闭都是直接致使当前窗口的数据库新建和销毁。兼容性以下:
Local做用于当前浏览器,即便你开2个Chrome浏览器(不是2个窗口)也仍是共用一个路径地址。永远不会自动删除,因此若是咱们要用LocalStorage保存敏感重要信息的时候也要注意不要放在Local里,而是放在Session里,关闭后进行清除,不然攻击者能够经过XSS攻击进行信息窃取。兼容性以下:
二者在PC端仅仅Chrome和Firefox有些许兼容误差,移动端兼容性相同。
PS:当浏览器进入隐私浏览模式,会建立一个新的、临时的数据库来存储local storage的数据;当关闭隐私浏览模式时,该数据库将被清空并丢弃。
只是API名字不一样。
localStorage.setItem('key', 'value'); // 设置
localStorage.getItem('key'); // 获取
localStorage.removeItem('key'); // 删除
localStorage.clear(); //清除全部复制代码
不过在储存和读取数据的时候,须要将数据进行JSON.stringify和JSON.parse,不然会强制改变数据类型
var obj = {
name:'掘金',
url:'juejin.com'
}
var arr = [1,2,3]
//错误方法
localStorage.setItem('object',obj); //object:"[object Object]" 没法读取
localStorage.setItem('array',arr); //array:"1,2,3" 变成string格式
//正确方法:Object
localStorage.setItem('object',JSON.stringify(obj));//存储 object:"{"name":"掘金","url":"juejin.com"}"
JSON.parse(localStorage.getItem('object'));//读取 {name: "掘金", url: "juejin.com"}
//正确方法:Array
localStorage.setItem('array',JSON.stringify(arr)); //存储 array:"[1,2,3]"
JSON.parse(localStorage.getItem('array'));//读取 [1,2,3]复制代码
复制代码
原本是用来作cookie的解决方案,适合于作小规模的简单结构数据储存与状态维护,不适宜储存敏感信息。能够运用在全部业务场景下。
IndexedDB是HTML5规范里新出现的浏览器里内置的数据库。跟NoSQL很像,提供了相似数据库风格的数据存储和使用方式。但IndexedDB里的数据是永久保存,适合于储存大量结构化数据,有些数据本应该存在服务器,可是经过indexedDB,能够减轻服务器的大量负担,实现本地读取修改使用,以对象的形式存储,每一个对象都有一个key值索引。
IndexedDB里的操做都是事务性的。一种对象存储在一个object store里,object store就至关于关系数据库里的表。IndexedDB能够有不少object store,object store里能够有不少对象。
首先来看兼容性
Window自带浏览器也只是部分支持,window8.1及以上才支持IE11。
首先,咱们建立一个数据库
//首先定义一个本身的版本
var my = {
name:'juejin',
version:'1',
db:null
}
//打开仓库,没有则建立
var request = window.indexedDB.open(my.name);
//window.indexedDB.open()的第二个参数即为版本号。在不指定的状况下,默认版本号为1.
//版本号不能是一个小数,不然会被转化成最近的整数。同时可能致使不会触发onupgradeneeded版本更新回调
console.log(request);复制代码
返回的是一个名字为IDBOpenDBRequest的对象。
里面有各个状态的回调参数,初始化的时候都是null,须要手动去挂载自定义的回调参数,从而实现window.indexedDB.open
函数的状态回调控制,再去控制台Appliation的indexedDB查看
咱们已经成功添加该名称为juejin的db对象了,security origin表示安全起源(我是在我的博客控制台进行建立的)
request.onerror = function(event){ //打开失败回调
console.log(`${my.name} open indexedDB is Fail`);
}
request.onsuccess = function(event){ //打开成功回调
console.warn(`${my.name} open indexedDB is success`);
//将返回的值赋给本身控制的db版本对象,下面两种方法都能接收到。
my.db = event.target.result|| request.result;
}
request.onupgradeneeded = function (event) {//版本变化回调参数,第一次设置版本号也会触发
console.log('indexDB version change');
}
console.log(my.db);复制代码
返回的是一个db对象,里面包含后续对该db对象状态控制的回调方法。这些方法仍然须要本身定义。
怎么关闭db对象和删除db对象呢?关闭和删除是两个概念。
//关闭db对象,以后没法对其进行插入、删除操做。
my.db.close();
//而删除方法则挂载在window.indexedDB下,删除该db仓库
window.indexedDB.deleteDatabase(my.db);复制代码
这里须要注意的一点是此onclose()
方法非上面代码调用的close()
方法,my.db.close()
调用的是__proto__原型内的方法。
知道如何创建和操做indexedDB以后,咱们对object store进行添加表的操做。上文咱们说到,indexedDB中没有表的概念,而是object store,一个数据库中能够包含多个object store,object store是一个灵活的数据结构,能够存放多种类型数据。也就是说一个object store至关于一张表,里面存储的每条数据和一个键相关联。
咱们可使用每条记录中的某个指定字段做为键值(keyPath
),也可使用自动生成的递增数字做为键值(keyGenerator
),也能够不指定。选择键的类型不一样,objectStore能够存储的数据结构也有差别。
建立object store对象只能从onupgradeneeded
版本变化回调中进行。
//建立object store对象
request.onupgradeneeded = function() {
var db = request.result;
var objectStore = db.createObjectStore("LOL", {keyPath: "isbn"});
var titleIndex = objectStore.createIndex("by_hero", "hero", {unique: true});
var authorIndex = objectStore.createIndex("by_author", "author");
objectStore.put({title: "亚索", author: "Roit", isbn: 123456});
objectStore.put({title: "提莫", author: "Roit", isbn: 234567});
objectStore.put({title: "诺手", author: "Hang", isbn: 345678});
};复制代码
createObjectStore
方法有2个参数,第一个表示该object store表的名称,第二个是对象,keyPath
为存储对象的某个属性(做为key值),options还有个参数:autoIncrement
表明是否自增。接下来创建索引
var titleIndex = objectStore.createIndex("by_title", "title", {unique: true});
var authorIndex = objectStore.createIndex("by_author", "author");复制代码
第一个参数是索引的名称,第二个参数指定了根据存储数据的哪个属性来构建索引,第三个options对象,其中属性unique
的值为true
表示不容许索引值相等。第二个索引没有options对象,接下来咱们能够经过put方法添加数据了。
objectStore.put({hero: "亚索", author: "Roit", isbn: 123456});
objectStore.put({hero: "提莫", author: "Roit", isbn: 234567});
objectStore.put({hero: "诺手", author: "Hang", isbn: 345678});复制代码
总体代码写上
var my = {//定义控制版本
name:'juejin',
version:'1',
db:null
};
var request = window.indexedDB.open(my.name); //建立打开仓库
request.onupgradeneeded = function() {//更新版本回调
var db = request.result;
var objectStore = db.createObjectStore("LOL", {keyPath: "isbn"});
var heroIndex = objectStore.createIndex("by_hero", "hero", {unique: true});
var authorIndex = objectStore.createIndex("by_author", "author");
objectStore.put({hero: "亚索", author: "Roit", isbn: 123456});
objectStore.put({hero: "提莫", author: "Roit", isbn: 234567});
objectStore.put({hero: "诺手", author: "Hang", isbn: 345678});
};
request.onsuccess = function() {//成功回调
my.db = event.target.result|| request.result;
console.warn(`${my.name} indexedDB is success open Version ${my.version}`);
};
request.onerror = function() {//失败回调
console.warn(`${my.name} indexedDB is fail open Version ${my.version}`);
};
复制代码
注意只有在运行环境下才会进行一个存储,本地打开静态文件是不会储存indexedDB的,虽然能弹出juejin indexedDB is success open
。这样咱们就成功建立了一个object store,咱们到控制台去看下
by_hero
表示在建立索引的时候,经过createObjectStore('by_hero','hero',{unique:true})
的时候,经过key值为hero
的对象,进行索引筛选的数据。再去by_author
看下,
同理,经过key值为author
的,进行索引的数据。这样就可以储存大量结构化数据。而且拥有索引能力,这一点比Storage强。固然,api也麻烦。接来下进行事务操做。
在IndexedDB
中,使用事务来进行数据库的操做。事务有三个模式,默认只读
readOnly
只读。readwrite
读写。versionchange
数据库版本变化//首先要建立一个事务,
var transaction = my.db.transaction('LOL', 'readwrite');
//获取objectStore数据
var targetObjectStore = transaction.objectStore('LOL');
//对预先设置的keyPath:isbn进行获取
var obj = targetObjectStore.get(345678);
//若是获取成功,执行回调
obj.onsuccess = function(e){
console.log('数据成功获取'+e.target.result)
}
//获取失败obj.onerror = function(e){
console.error('获取失败:'+e.target.result)
}复制代码
获取成功,拿到isbn为345678的数据。
第一个参数为须要关联的object store名称,第二个参数为事务模式,选择可读可写,与indexedDB同样,调用成功后也会触发onsuccess、onerror回调方法。能够读取了咱们尝试去添加
targetObjectStore.add({hero: "盖伦", author: "Yuan", isbn: 163632});
targetObjectStore.add({hero: "德邦", author: "Dema", isbn: 131245});
targetObjectStore.add({hero: "皇子", author: "King", isbn: 435112});复制代码
有一点要注意,添加剧复数据会更新。添加完毕后,去控制台看下
不对啊,确定有的,刷新无数遍后,终于找到了解决办法。这多是Chrome的一个BUG吧。
删除数据
//获取数据是get,删除数据是delete
targetObjectStore.delete(345678);
复制代码
一样 ,须要输入筛选数据才会触发刷新,不过在平常中已经足够咱们使用。
更新数据
var obj = targetObjectStore.get(123456);
//若是获取成功,执行回调
obj.onsuccess = function(e){
console.log('数据成功获取'+e.target.result);
var data = e.target.result;
data.hero = '亚索踩蘑菇挂了';
//再put回去
var updata = targetObjectStore.put(data);
updata.onsuccess = function(event){
console.log('更新数据成功'+event.target.result);
}
}复制代码
又要筛选一遍.
当你须要便利整个存储空间中的数据时,你就须要使用到游标。游标使用方法以下:
var request = window.indexedDB.open('juejin');
request.onsuccess = function (event) {
var db = event.target.result;
var transaction = db.transaction('LOL', 'readwrite');
//获取object store数据
var objectStore = transaction.objectStore('LOL');
//获取该数据的浮标
var eachData = objectStore.openCursor();
//openCursor有2个参数(遍历范围,遍历顺序)
eachData.onsuccess = function (event) {
var cursor = event.target.result;
if (cursor){
console.log(cursor);
cursor.continue();
}
};
eachData.onerror = function (event) {
consoe.error('each all data fail reason:'+event.target.result);
};
}复制代码
这样经过openCursor
获得的数据就相似于forEach
输出,当表中无数据,仍会书法一次onsuccess
回调
上面提到openCursor
的两个参数,第一个是遍历范围,由indexedDB的 :IDBKeyRange
的API进行实现,主要有如下几个值
//区间向上匹配,第一个参数指定边界值,第二个参数是否包含边界值,默认false包含。
lowerBound('边界值',Boolean);
var index = IDBKeyRange.lowerBound(1);//匹配key>=1
var index = IDBKeyRange.lowerBound(1,true);//匹配key>1
//单一匹配,指定参数值
only('值');
var index = IDBKeyRange.only(1);//匹配key===1;
//区间向下搜索,第一个参数指定边界值,第二个参数是否包含边界值,默认false包含。
upperBound('边界值',Boolean);
var index = IDBKeyRange.upperBound(2);//匹配key<=2
var index = IDBKeyRange.upperBound(2,true);//匹配key<2
//区间搜索,第一个参数指定开始边界值,第二个参数结束边界值,
// 第三个指定开始边界值是否包含边界值,默认false包含。第四个指定结束边界值是否包含边界值,默认false
bound('边界值',Boolean);
var index = IDBKeyRange.bound(1,10,true,false);//匹配key>1&&key<=10;复制代码
openCursor第二个参数,遍历顺序,指定游标遍历时的顺序和处理相同id(keyPath属性指定字段)重复时的处理方法。改范围经过特定的字符串来获取。其中:
IDBCursor.next
,从前日后获取全部数据(包括重复数据)IDBCursor.prev
,从后往前获取全部数据(包括重复数据)IDBCursor.nextunique
,从前日后获取数据(重复数据只取第一条)IDBCursor.prevunique
,从后往前获取数据(重复数据只取第一条)咱们来试一下
var request = window.indexedDB.open('juejin');
request.onsuccess = function (event) {
var db = event.target.result;
var transaction = db.transaction('LOL', 'readwrite');
//获取object store数据
var objectStore = transaction.objectStore('LOL');
//bound('边界值',Boolean);匹配key>22000&&key<=400000;
var index = IDBKeyRange.bound(220000,400000,true,false);
//获取该数据的浮标,从前日后顺序索引,包括重复数据
var eachData = objectStore.openCursor(index,IDBCursor.NEXT);
eachData.onsuccess = function (event) {
var cursor = event.target.result;
console.log(cursor);
if (cursor) cursor.continue();
};
eachData.onerror = function (event) {
consoe.error('each all data fail reason:'+event.target.result);
};
}
复制代码
搜索key值为220000到40000万之间的数据,搜索出一条。
好了,indexedDB基本和事务操做讲的差很少了,如今说说它另外一方面:
目前为止,所知道在IndexedDB
中,键值对中的key
值能够接受如下几种类型的值:
可是储存数据千万要注意的一点是,若是储存了isbn
相同的数据,是无效操做,甚至可能引发报错。
keyPath
可以接受的数据格式,示例中createObjectStore
时设置的{KeyPath:'isbn'}
,为主键
至于value几乎能接受全部数据格式。
IndexedDB
时,可能会出现因为没有权限而致使的异常(LocalStorage也会),须要进行异常处理。IndexedDB
中相关的数据。什么是ServiceWork?serviceWork是W3C 2014年提出的草案,是一种独立于当前页面在后台运行的脚本。这里的后台指的是浏览器后台,可以让web app拥有和native app同样的离线程序访问能力,让用户可以进行离线体验,消息推送体验。service worker是一段脚本,与web worker同样,也是在后台运行。做为一个独立的线程,运行环境与普通脚本不一样,因此不能直接参与web交互行为。native app能够作到离线使用、消息推送、后台自动更新,service worker的出现是正是为了使得web app也能够具备相似的能力。
打开了如今浏览器单线程的革面,随着前端性能愈来愈强,要求愈来愈高,咱们都知道在浏览器中,JavaScript是单线程执行的,若是涉及到大量运算的话,颇有可能阻碍css tree的渲染,从而阻塞后续代码的执行运算速度,ServiceWork的出现正好解决了这个问题,将一些须要大规模数据运算和获取 资源文件在后台进行处理,而后将结果返回到主线程,由主线程来执行渲染,这样能够避免主线程被巨量的逻辑和运算所阻塞。这样的大大的提高了JavaScript线程在处理大规模运算时候的一个能力, 这也是ServiceWork自己的巨大优点,好比咱们要进行WebGBL场景下3D模型和数据的运算,一个普通的数据可能高达几MB,若是放在主线程进行运算的话,会严重阻碍页面的渲染,这个时候就很是适合ServiceWork进行后台计算,再将结果返回到主线程进行渲染。
service worker能够:
说了这么多,到底跟咱们实际工做中有什么用处呢,这里就要介绍google 的PWD(Progressive Web Apps),它是一种Web App新模型,渐进式的web App,它依赖于Service Work,是如今没有网络的环境中也可以提供基本的页面访问,不会出现‘未链接到互联网’,能够优化网页渲染及网络数据访问,而且能够添加到手机桌面,和普通应用同样有全屏状态和消息推送的功能。
这是Service Work
的生命周期,首先没有Service Work的状况下会进行一个安装中的状态,返回一个promise
实例,reject的会走到Error这一步,resolve
安装成功,当安装完成后,进入Activated
激活状态,在这一阶段,你还能够升级一个service worker
的版本,具体内容咱们会在后面讲到。在激活以后,service worker
将接管全部在本身管辖域范围内的页面,可是若是一个页面是刚刚注册了service worker
,那么它这一次不会被接管,到下一次加载页面的时候,service worker
才会生效。当service worker
接管了页面以后,它可能有两种状态:要么被终止以节省内存,要么会处理fetch
(拦截和发出网络请求)和message
(信息传递)事件,这两个事件分别是页面初始化的时候产生了一个网络请求出现或者页面上发送了一个消息。
目前有哪些页面支持service Work
呢?
在Chrome浏览器地址栏输入chrome://inspect/#service-workers
,能够看到目前为止你访问过全部支持service work
的网站
你也能够打开控制台,到Application,点击serviceWork这一栏,
那怎么才能体验到service Work呢,咱们以Vue官网为例,首先打开https://cn.vuejs.org/,等待加载完成,如今关掉你的WiFi和全部能连上互联网的工具。再刷新地址栏页面
是否是感受很新奇,怎么作到呢,继续往下看。须要运行本地环境,各类方法自行百度,我使用的是本身购买的腾讯云服务器,nginx多开几个端口,SFTP自动上传。也能够搭建本地localhost
,切记不能够用IP地址,ServiceWork
不支持域名为IP的网站,作好这些咱们开始。
首先建立一个文件夹,再建立index.html
,index.css
,app.js
,servicework.js
这些文件咱们后续都要用到。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ServiceWork</title>
</head>
<body>
<h1>ServiceWork</h1>
</body>
<script src="./app.js"></script>
<link rel="stylesheet" href="./index.css">
</html>复制代码
引入了一个main.css文件和一个app.js
main.css
h1{
color:red;
text-align:center;
}复制代码
app.js
alert(1);复制代码
测试成功弹出alert(1)后,咱们开始写代码。
首先要肯定是否支持ServiceWork
。
app.js
if (navigator.serviceWorker) {
//先注入注册文件,第二个参数为做用域,为当前目录
navigator.serviceWorker.register('./servicework.js', {
scope: './'
}).then(function (reg) {
console.warn('install ServiceWork success',reg)
}).catch(function (err) {
console.error(err)
})
} else {
//不支持serviceWork操做
}复制代码
导入注册配置文件,返回一个promise,触发相应回调,而后再去修改servicework.js
文件,
//self是serviceWorker的实例。
console.log(self)//
给实例监听安装事件,成功触发回调self.addEventListener('install', function (e) {
//ExtendableEvent.waitUntil()扩展事件的生命周期。
e.waitUntil(
//咱们经过打开名称为'app-v1'的缓存,将读取到的文件储存到cache里
caches.open('app-v1').then(function (cache) {
console.log('caches staticFile success');
//添加cache
return cache.addAll([
'./app.js',
'./servicework.html',
'./servicework.js',
'./index.css'
]);
})
);
});
复制代码
ExtendableEvent.waitUntil()
接受一个promise
对象,它能够扩展时间的生命周期,延长事件的寿命从而阻止浏览器在事件中的异步操做完成以前终止服务工做线程。它能够扩展时间的生命周期,延长事件的寿命从而阻止浏览器在事件中的异步操做完成以前终止服务工做线程。
install
的时候,它会延迟将安装的works
视为installing
,直到传递的Promise
被成功地resolve
。确保服务工做线程在全部依赖的核心cache被缓存以前都不会被安装。
一旦 Service Worker
成功安装,它将转换到Activation
阶段。若是之前的 Service Worker
还在服务着任何打开的页面,则新的 Service Worker
进入 waiting
状态。新的 Service Worker
仅在旧的 Service Worker
没有任何页面被加载时激活。这确保了在任什么时候间内只有一个版本的 Service Worker 正在运行。当进行
activited
的时候,它延迟将
active work
视为已激活的,直到传递的
Promise
被成功地
resolve
。确保功能时间不会被分派到
ServiceWorkerGlobalScope
对象。
成功丢到缓存里后,就可使用fetch进行网络拦截了。
//一样的方法,监听fetch事件,
self.addEventListener('fetch', function (event) {
//respondWith方法产生一个request,response。
event.respondWith(
//利用match方法对event.request所请求的文件进行查询
caches.match(event.request).then(
function (res) {
console.log(res, event.request);
//若是cache中有该文件就返回。
if (res) {
return res
} else {
//没有找到缓存的文件,再去经过fetch()请求资源
fetch(res.url).then(function (res) {
if (res) {
if (!res || res.status !== 200 || res.type !== 'basic') {
return res;
}
//再将请求到的数据丢到cache缓存中..
var fetchRequest = event.request.clone();
var fileClone = res.clone();
caches.open('app-v1')
.then(function (cache) {
cache.put(event.request, fileClone);
});
} else {
//没有请求到该文件,报错处理
console.error('file not found:' + event.reuqest + '==>' + res.url)
}
})
}
}
)
);
});复制代码
对于前端你们确定很熟悉request
,response
表明着什么,event.respondWith()
会根据当前控制的页面产生一个request
,request
再去生成自定义的response
,network error
或者 Fetch
的方式resolve
。
fetch()对网络进行拦截,代理请求,先读取本地文件,没有资源再去请求,很大程度的节约了网络请求消耗。
如今咱们去试试有没有成功!
啊哈,漂亮!这样就实现了离线访问,可是在实际项目中,尽可能不要缓存servicework.js文件,可能没法及时生效,进行后续修改。咱们去控制台看下
已经安装好了,而且在运行中。
总体大概的流程以下
Service Work.js 的更新不只仅只是简单的更新,为了用户可靠性体验,里面仍是有不少门道的。
install
事件被触发waiting
状态。注意,此时并不存在替换terminated
后,新的 ServiceWork 才会发生做用。具体行为就是,该网页被关闭一段时间,或者手动的清除 service worker
。而后,新的 Service Work 就度过可 waiting
的状态。activate
事件。整个流程图为:
一个版本的缓存建立好以后,咱们也能够设置多个缓存,那怎去删除不在白名单中的缓存呢
self.addEventListener('activate', function(event) {
//上个版本,咱们使用的是'app-v1'的缓存,因此就须要进行清除,进行'app-v2'版本的缓存储存
var cacheWhitelist = ['app-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});复制代码
若是 new service work
要当即生效呢,那就要用到skipWaiting,
在 install
阶段使用 self.skipWaiting();
,由于上面说到 new Service Work 加载后会触发 install
而后进入 waiting
状态。那么,咱们能够直接在 install
阶段跳过等待,直接让 new Service Work 进行接管。
self.addEventListener('install',function(event) {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});复制代码
上面的 service work
更新都是在一开始加载的时候进行的,那么,若是用户须要长时间停留在你的网页上,有没有什么手段,在间隔时间检查更新呢?
有的,可使用 registration.update()
来完成。
navigator.serviceWorker.register('./ServiceWork.js').then(function(reg){
// sometime later…
reg.update();
});复制代码
另外,若是你一旦用到了 ServiceWork.js
而且肯定路由以后,请千万不要在更改路径了,由于,浏览器判断 ServiceWork.js
是否更新,是根据 ServiceWork.js
的路径来的。若是你修改的 ServiceWork.js
路径,而之前的 ServiceWork.js
还在做用,那么你新的 ServiceWork
永远没法工做。除非你手动启用 update
进行更新。
你想要一个文件更新,只须要在 ServiceWork 的 fetch
阶段使用 caches 进行缓存便可。一开始咱们的 install
阶段的代码为:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('app-v1').then(function(cache) {
return cache.addAll([
'./app.js',
'./servicework.html',
'./index.css'
]);
})
);
});
复制代码
self.addEventListener('install', function (event) {
var now = Date.now();
// 事先设置好须要进行更新的文件路径
var urlsToPrefetch = [
'./index.css',
'./servicework.html'
];
event.waitUntil(
caches.open(CURRENT_CACHES.prefetch).then(function (cache) {
var cachePromises = urlsToPrefetch.map(function (urlToPrefetch) {
// 使用 url 对象进行路由拼接
var url = new URL(urlToPrefetch, location.href);
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
// 建立 request 对象进行流量的获取
var request = new Request(url, {
mode: 'no-cors'
});
// 手动发送请求,用来进行文件的更新
return fetch(request).then(function (response) {
if (response.status >= 400) {
// 解决请求失败时的状况
throw new Error('request for ' + urlToPrefetch +
' failed with status ' + response.statusText);
}
// 将成功后的 response 流,存放在 caches 套件中,完成指定文件的更新。
return cache.put(urlToPrefetch, response);
}).catch(function (error) {
console.error('Not caching ' + urlToPrefetch + ' due to ' + error);
});
});
return Promise.all(cachePromises).then(function () {
console.log('Pre-fetching complete.');
});
}).catch(function (error) {
console.error('Pre-fetching failed:', error);
})
);
});复制代码
传送门Github查看该段代码。
当成功获取到缓存以后, ServiceWork 并不会直接进行替换,他会等到用户下一次刷新页面事后,使用新的缓存文件。
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('app-v1').then(function(cache) {
return cache.match(event.request).then(function(response) {
var fetchPromise = fetch(event.request).then(function(res) {
cache.put(event.request, res.clone());
return res;
})
return response || fetchPromise;
})
})
);
});复制代码
更详细的其余方法运用能够参考这篇文章.
本文只讲本地缓存,以后会将地理围栏、消息推送相关信息更新在博客,有兴趣的朋友能够收藏本文章,更具体规范的代码内容能够到这查看。
service work(PWA)缺点:
Uncaught (in promise) DOMException: Quota exceeded.
异常。清理后必需要重启浏览器才生效。优势:
如上文所述,有着消息推送、网络拦截代理、后台运算、离线缓存、地理围栏等很实用的一些技术。
本文参考了不少大神的代码,不喜勿喷,诚心学习请指教。