前言: 看到一篇讲解service worker的文章, 基础讲的还不错, 因此转了之后做为本身的参考css
service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。它的特性将包括推送消息,背景后台同步, geofencing(地理围栏定位),拦截和处理网络请求。web
这个 API 会让人兴奋的缘由是,它可使你的应用先访问本地缓存资源,因此在离线状态时,在没有经过网络接收到更多的数据前,仍能够提供基本的功能(通常称之为 Offline First)。编程
在 service worker 以前,另外一个叫作 APP Cache 的 api 也能够提供离线体验。APP Cache 的的主要问题是坑比较多,并且其被设计为只适合于单页 web 应用程序,对于传统的多页网站则不适合。service worker 的设计规避了这些痛点。json
关于 service worker 的一些注意点:api
if('caches' in window) { // Has support! }
caches.open('test-cache').then(function(cache) { // Cache is created and accessible });
caches.open('test-cache').then(function(cache) { cache.addAll(['/', '/images/logo.png']) .then(function() { // Cached! // or use cache.add cache.add('/page/1'); // "/page/1" URL will be fetched and cached! }); });
caches.open('test-cache').then(function(cache) { cache.keys().then(function(cachedRequests) { console.log(cachedRequests); // [Request, Request] }); });
caches.open('test-cache').then(function(cache) { cache.match('/page/1').then(function(matchedResponse) { console.log(matchedResponse); }); });
(6)cache.delete,删除缓存。数组
caches.open('test-cache').then(function(cache) { cache.delete('/page/1'); });
// url (required), options (optional) fetch('https://davidwalsh.name/some/url', { method: 'get' }).then(function(response) { }).catch(function(err) { // Error :( });
其中options对象包含如下属性:promise
能够在fetch中传入Request对象实例:浏览器
var request = new Request('https://davidwalsh.name/users.json', { method: 'POST', mode: 'cors', redirect: 'follow', headers: new Headers({ 'Content-Type': 'text/plain' }) }); // Now use it! fetch(request).then(function() { /* handle response */ });
能够自定义返回的Response对象实例,其中的options有:缓存
另外Response的实例还具有如下方法:服务器
// Create your own response for service worker testing // new Response(BODY, OPTIONS) var response = new Response('.....', { ok: false, status: 404, url: '/' }); // The fetch's `then` gets a Response instance back fetch('https://davidwalsh.name/') .then(function(responseObj) { console.log('status: ', responseObj.status); });
self.importScripts('./serviceworker-cache-polyfill.js');
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch(function(err) { // registration failed :( console.log('ServiceWorker registration failed: ', err); }); }
上面的代码检查 service worker API 是否可用,若是可用, /sw.js 这个文件将会做为 service worker 被注册。
若是这个 service worker 已经被注册过,浏览器会自动忽略上面的代码。
有一个特别要注意是 service worker 文件的路径。你必定注意到,在这个例子中,service worker 文件被放在这个域的根目录下,这意味着 service worker是跟网站同源的。换句话说,这个 service worker 将会获取到这个域下的全部 fetch 事件。若是 service worker文件注册到/example/sw.js ,那么 service worker 只能收到 /example/ 路径下的 fetch 事件(好比: /example/page1/, /example/page2/)。
var CACHE_NAME = 'my-site-cache-v1'; var urlsToCache = [ '/', '/styles/main.css', '/script/main.js' ]; self.addEventListener('install', function(event) { // Perform install steps event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); }) ); });
上面代码声明了须要缓存的内容,若是全部的文件都缓存成功,service worker 就安装成功了。若是任何一个文件下载失败,那么安装步骤就会失败。这个方式依赖于你本身指定的资源,但这意味着,你须要很是仔细地肯定哪些文件须要被缓存。指定了太多文件的话,会增长失败率。
self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } // IMPORTANT: Clone the request. A request is a stream and // can only be consumed once. Since we are consuming this // once by cache and once by the browser for fetch, we need // to clone the response var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; } // IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) );
若是咱们想在缓存中添加新的请求缓存,能够经过处理fetch请求的response,将其添加到缓存中便可。代码里咱们作了如下事情:
添加一个 callback 到 fetch 请求的 .then 方法中。 一旦咱们得到一个 response,咱们进行以下的检查:
1. 确保 response 有效
2. 检查 response 的状态是200
3. 确保 response 的类型是 basic 类型的,这说明请求是同源的,这意味着第三方的请求不能被缓存。
若是检查经过会clone 这个请求。这么作的缘由是若是 response 是一个 Stream,那么它的 body 只能被消费一次。因此为了让浏览器跟缓存都使用这个body,咱们必须克隆这个 body,一份到浏览器,一份到缓存中缓存。
你的 service worker 总会有要更新的时候。在那时,你须要按照如下步骤来更新:
以前咱们使用的缓存能够叫 my-site-cache-v1 ,咱们想把这个拆封到多个缓存,一份给页面使用,一份给博客文章使用。这意味着,install 步骤里,咱们要建立两个缓存: pages-cache-v1 和 blog-posts-cache-v1。在 activite 步骤里,咱们须要删除旧的 my-site-cache-v1。
下面的代码会遍历全部的缓存,并删除掉不在 cacheWhitelist 数组(咱们定义的缓存白名单)中的缓存。
self.addEventListener('activate', function(event) { var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1']; event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });