Service Worker API带有一个Cache接口,可以让您创建按请求键入的响应存储。 缓存资源的常见模式有:service worker安装、用户交互、网络响应。
1、在service worker安装的时候缓存应用程序脚本
我们可以在这里缓存Html、css、js等一些静态资源。
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(
[
'/css/bootstrap.css',
'/css/main.css',
'/js/bootstrap.min.js',
'/js/jquery.min.js',
'/offline.html'
]
);
})
);
});
在第一次安装service worker的时候触发
注意:需要注意的是,当这个事件发生的时候,你的service worker的任何以前的版本仍然在运行和提供页面,所以你在这里做的事情不能破坏这个。 例如,这不是删除旧缓存的好地方,因为之前的service worker可能仍然在使用它们。
2.用户交互时
如果整个网站无法脱机,则可以让用户选择要脱机使用的内容(例如,视频,文章或照片库)。
一种方法是给用户一个“稍后阅读”或“保存离线”按钮。 点击时,从网络中获取所需内容并将其放入缓存中:
document.querySelector('.cache-article').addEventListener('click', function(event) {
event.preventDefault();
var id = this.dataset.articleId;
caches.open('mysite-article-' + id).then(function(cache) {
fetch('/get-article-urls?id=' + id).then(function(response) {
// /get-article-urls returns a JSON-encoded array of
// resource URLs that a given article depends on
return response.json();
}).then(function(urls) {
cache.addAll(urls);
});
});
});
注意:缓存API在window对象上可用,这意味着您不需要service worker将内容添加到缓存中。
3.在网络响应时
如果请求与缓存中的任何内容都不匹配,则从网络获取该请求,将其发送到页面并同时将其添加到缓存中。
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
这种方法最适合频繁更新的资源,例如用户的收件箱或文章内容。 这对非必要的内容(例如头像)也是有用的,但需要小心。
注意:为了提高内存使用效率,您只能读取一次响应/请求的主体。 在上面的代码中,.clone()用于创建可单独读取的响应的副本。
从缓存中提取文件
要从缓存提供内容并使您的应用脱机,您需要拦截网络请求并使用缓存中存储的文件进行响应。 有几种方法:
- cache only
- network only
- cache falling back to network
- network falling back to cache
- cache then network
仅从缓存中
你不需要经常专门处理这个文件。回退到网络的缓存通常是适当的方法。
这种方法适用于作为应用程序主代码(应用程序的“版本”的一部分)的任何静态资源。你应该在安装事件中缓存这些,所以你可以在那里依赖它们。
self.addEventListener('fetch',function(event){
event.respondWith(caches.match(event.request));
});
如果在缓存中找不到匹配项,则响应看起来像连接错误。
仅从网络中
对于不能脱机执行的事情,这是正确的方法,例如分析ping和非GET请求。同样,你并不需要经常专门处理这个情况,并且先从缓存再到网络的方法通常会更合适。
self.addEventListener('fetch',function(event){
event.respondWith(fetch(event.request));
});
或者,不要调用event.respondWith,因为这将导致默认的浏览器行为。
先缓存再回到网络
如果您先让应用程序离线,那么您将如何处理大部分请求。其他模式传入的请求将会异常。
self.addEventListener('fetch',function(event){
event.respondWith(
caches.match(event.request).then(function(response){
return response||fetch(event.request);
})
);
});
这为缓存中的内容提供了“Cache only”行为,而对于任何未缓存的内容(包括所有非GET请求,因为它们不能被缓存),“Network only”行为都是如此。
先网络再回到缓存
对于经常更新的资源来说,这是一个很好的方法,而不是网站“版本”(例如,文章,头像,社交媒体时间表,游戏排行榜)的一部分。以这种方式处理网络请求意味着在线用户获得最新的内容,离线用户获得较旧的缓存版本。
但是,这种方法有缺陷。如果用户连接中断或连接速度较慢,则在从缓存中获取内容之前,必须等待网络请求失败。这可能需要很长时间,并且是令人沮丧的用户体验。查看下一个方法,缓存然后网络,以获得更好的解决方案。
self.addEventListener('fetch',function(event){
event.respondWith(
fetch(event.request).catch(function(){
return caches.match(event.request);
})
);
});
在这里,我们首先使用fetch()将请求发送到网络,并且只有在失败的情况下才能在缓存中查找响应。
缓存然后网络
对于经常更新的资源来说,这也是一个很好的方法。这种方法将尽可能快地在屏幕上获得内容,但是一旦到达,仍然显示最新的内容。
这要求页面发出两个请求:一个到缓存,一个到网络。这个想法是首先显示缓存的数据,然后在/如果网络数据到达时更新页面。
这是页面中的代码:
var networkDataReceived = false;
startSpinner();
// fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage(data);
});
// fetch cached data
caches.match('/data.json').then(function(response) {
if (!response) throw Error("No data");
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner());
我们同时向网络和缓存发送请求。缓存将最有可能首先响应,如果网络数据还没有收到,我们用缓存响应中的数据更新页面。当网络响应时,我们再次用最新的信息更新页面。
以下是service worker的代码:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
})
);
});
获取到新数据的时候缓存网络响应数据。
有时您可能在新数据到达时替换当前数据(例如,游戏排行榜),但要小心,不要隐藏或替换用户可能与之交互的内容。例如,如果您从缓存中加载博客文章的页面,然后将新帖子添加到网页的顶部,那么您可以考虑调整滚动位置,以便用户不中断。如果您的应用程序布局非常线性,这可能是一个很好的解决方案
通用回退方法
如果您无法从缓存或网络提供某些内容,则可能需要提供一个通用回退方法。此技术适用于辅助图像,如头像,失败的POST请求,“离线时不可用”页面。
self.addEventListener('fetch', function(event) {
event.respondWith(
// Try the cache
caches.match(event.request).then(function(response) {
// Fall back to network
return response || fetch(event.request);
}).catch(function() {
// If both fail, show a generic fallback:
return caches.match('/offline.html');
// However, in reality you'd have many different
// fallbacks, depending on URL & headers.
// Eg, a fallback silhouette image for avatars.
})
);
});
您回退的项目很可能是安装依赖项。
您还可以根据网络错误提供不同的回退:
self.addEventListener('fetch', function(event) {
event.respondWith(
// Try the cache
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
return fetch(event.request).then(function(response) {
if (response.status === 404) {
return caches.match('pages/404.html');
}
return response
});
}).catch(function() {
// If both fail, show a generic fallback:
return caches.match('/offline.html');
})
);
});
网络响应错误不会在fetch promise中抛出错误。 相反,fetch返回包含网络错误的错误代码的响应对象。 这意味着我们在.then而不是.catch中处理网络错误。
删除过期的缓存
一旦安装了新的service worker并且以前的版本没有被使用,新的service worker就会激活,并且您将获得一个激活事件。 由于旧版本没有被使用,现在是删除未使用的缓存的好时机。
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});
在激活期间,其他事件(如fetch)被放入队列中,所以长时间的激活可能会阻止页面加载。 保持激活尽可能精简,只用于旧版本激活时无法执行的操作。
参考链接:https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker#using_the_cache_api