前言: 看到一篇講解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); } }) ); }) ); });