service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。它的特性将包括推送消息,背景后台同步, geofencing(地理围栏定位),拦截和处理网络请求。javascript
这个 API 会让人兴奋的缘由是,它可使你的应用先访问本地缓存资源,因此在离线状态时,在没有经过网络接收到更多的数据前,仍能够提供基本的功能(通常称之为 Offline First)。css
在 service worker 以前,另外一个叫作 APP Cache 的 api 也能够提供离线体验。APP Cache 的的主要问题是坑比较多,并且其被设计为只适合于单页 web 应用程序,对于传统的多页网站则不适合。service worker 的设计规避了这些痛点。java
关于 service worker 的一些注意点:git
注册service worker,在网页上生效github
安装成功,激活 或者 安装失败(下次加载会尝试从新安装)web
激活后,在sw的做用域下做用全部的页面,首次控制sw不会生效,下次加载页面才会生效。编程
sw做用页面后,处理fetch(网络请求)和message(页面消息)事件 或者 被终止(节省内存)。json
(1)检测api是否存在api
if('caches' in window) { // Has support! }
(2)caches.open,建立缓存总对象。以下建立名为 test-cache 的缓存。数组
caches.open('test-cache').then(function(cache) { // Cache is created and accessible });
(3)cache.add和cache.addAll,添加缓存内容。其中cache.add只添加一个,cache.addAll能够添加多个。
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! }); });
(4)cache.keys(),查看已经缓存的数据
caches.open('test-cache').then(function(cache) { cache.keys().then(function(cachedRequests) { console.log(cachedRequests); // [Request, Request] }); });
(5)cache.match和cache.matchAll,匹配缓存文件路径
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对象包含如下属性:
能够在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 */ });
(3)能够自定义返回的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,咱们进行以下的检查:
若是检查经过会clone 这个请求。这么作的缘由是若是 response 是一个 Stream,那么它的 body 只能被消费一次。因此为了让浏览器跟缓存都使用这个body,咱们必须克隆这个 body,一份到浏览器,一份到缓存中缓存。
你的 service worker 总会有要更新的时候。在那时,你须要按照如下步骤来更新:
更新你 service worker 的 JavaScript 文件 当用户浏览你的网站时,浏览器尝试在后台从新下载 service worker 的脚本文件。通过对比,只要服务器上的文件和本地文件有一个字节不一样,这个文件就认为是新的。
以后更新后的 service worker 启动并触发 install 事件。
此时,当前页面生效的依然是老版本的 service worker,新的 service worker 会进入 “waiting” 状态。
当页面关闭以后,老的 service worker 会被干掉,新的 servicer worker 接管页面 一旦新的 service worker 生效后会触发 activate 事件。 一般来说,须要在 activate 的 callback 中进行 cache 管理,来清理老的 cache。咱们在 activate 而不是 install 的时候进行的缘由,是若是咱们在 install 的时候进行清理,那么老的 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); } }) ); }) ); });
http://kailian.github.io/2017/03/01/service-worker
http://www.javashuo.com/article/p-yceutgan-he.html
https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers
https://lavas.baidu.com/doc/offline-and-cache-loading/service-worker/how-to-use-service-worker
https://zhuanlan.zhihu.com/p/20040372
https://zhuanlan.zhihu.com/p/28161855
https://www.villainhr.com/page/2017/01/08/Service Worker 全面进阶