示例代码
sw.js只会拦截当前目录或者后代目录的http请求,如果要缓存根目录文件(index.html),必须把sw.js放在根目录.
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(worker => { }).catch(e => console.log('reg errot', e)); }
sw.js
var CACHE_PREX = 'DQM_'; //定义一个缓存名前缀
var CACHE_NAME = CACHE_PREX+'v4';//定义缓存版本
var CACHE_PATH = serviceWorker.scriptURL.split('/').slice(0,-1).join('/')+'/'; //获取URL目录
var urlsToCache = [//定义要缓存的文件列表,可选,不设置也可以.具体看 fetch
'favicon.ico',
'assets/dqm.png',
];
Object.entries(
{
install(event){
//注册,如果本脚本发生改变 会重新注册
console.log('serviceWorker install');
return self.skipWaiting();//跳过等待 如果不跳过则可以进行预先缓存
event.waitUntil(
caches.open(CACHE_NAME).then(
cache=>cache.addAll(urlsToCache)
//这里意思是立即吧缓存列表下载下来.这样缺点是屏幕会根据时间长时间空白,但是确保所有文件都缓存了.
//这里的缓存是可以跨目录的,而是fetch事件只能拦截sw.js所在目录的的请求.
//对于新手来说就会出现明明缓存了,离线失败
//对于老手来说,这里换成一个奇怪文件A,读取B url时返回A内容 fetch事件:response = await (await caches.open(CACHE_NAME)).get(A url)
).then(() => {
console.log('Cache downloaded',caches)
self.skipWaiting()
})
);
},
activate(event){
//激活变化 初始化
//清空特定数据const cache = await caches.open(CACHE_NAME);cache.delete(url);
console.log('serviceWorker activate');
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (CACHE_NAME != cacheName&&cacheName.includes(CACHE_PREX)) {
//移除特定旧缓存数据库
return caches.delete(cacheName);
}
})
);
})
);
},
fetch(event){
//拦截请求 event.request 一个请求对象
return event.respondWith(new Promise(async resolve=>{
var url = event.request.url.replace(CACHE_PATH,''),cacheTime;
var response = await caches.match(event.request); //看看缓存里面有没有我缓存的文件
if(navigator.onLine){
//联网状态
if(response){
/*
//缓存里面找到数据
//response.headers.get('date') 缓存里面的保存日期
if(new Date() - Date.parse(response.headers.get('date'))>86400){
fetch(event.request).then(async res=>await caches.open(CACHE_NAME).put(event.request, res.clone()))
// 后台更新 当超过缓存时间时,后天刷新记录,
}
*/
/*
//当然用户刷新浏览器时采用新数据,当然你也可以立即应用新的(如果文件较大推荐后台更新)
if(new Date() - Date.parse(response.headers.get('date'))>86400) response = null;
*/
}
if(!response){
//没有匹配到数据 进行正常下载
response = await fetch(event.request);
if(urlsToCache.includes(event.request.url.replace(CACHE_PATH,''))){
//如果 install 中已经缓存,这一步可以删掉.
//缓存条件 这里的缓存条件就是我上面的缓存列表,
//当然你可以额外增加图片后缀一律缓存亦可
const cache = await caches.open(CACHE_NAME); //打开缓存表
console.log('[Service Worker] Caching new resource: ' + url);
cache.put(event.request, response.clone()); //写入缓存 其他方法match get keys(都是异步)
//这里的 表cache.match与caches.match不一样的,前者只有数据,后者含有请求的headers
}
}
}
resolve(response);//返回数据
}));
},
message(event){
console.log(event.data,event.source);
//处理接到的信息
//对信息源通信 event.source.postMessage()
//注意如果使用这个通信 ,不要使用 worker.postMessage/self.postMessage 否则会陷入无限循环,因为他也会触发message
//主线程用 navigator.serviceWorker.controller.postMessage 发给worker信息
//主线程 navigator.serviceWorker.addEventListener('message') 接收
//所以woker是不能主动对主线程发起通信.
}
}
).forEach(
entry=>{
self.addEventListener(entry[0],entry[1]);
}
);
caches与indexedb
caches也是一种缓存,但是储存请求.
caches.match(url)
匹配一个请求,而存储时需要先打开一个版本,再去写入,如果存在不同版本则先打开版本再去match.
match的结果包含数据headers,而cache.get获取的只有数据流的对象.
const cache = await caches.open(CACHE_NAME); const request = await fetch(url); cache.put(request.url, request.clone()); cache.match(url);