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 全面進階