service worker的基本知識

前言: 看到一篇講解service worker的文章, 基礎講的還不錯, 因此轉了之後做爲本身的參考css

Service Worker是什麼

service worker 是獨立於當前頁面的一段運行在瀏覽器後臺進程裏的腳本。它的特性將包括推送消息,背景後臺同步, geofencing(地理圍欄定位),攔截和處理網絡請求。web

這個 API 會讓人興奮的緣由是,它可使你的應用先訪問本地緩存資源,因此在離線狀態時,在沒有經過網絡接收到更多的數據前,仍能夠提供基本的功能(通常稱之爲 Offline First)。編程

在 service worker 以前,另外一個叫作 APP Cache 的 api 也能夠提供離線體驗。APP Cache 的的主要問題是坑比較多,並且其被設計爲只適合於單頁 web 應用程序,對於傳統的多頁網站則不適合。service worker 的設計規避了這些痛點。json

關於 service worker 的一些注意點:api

  • service worker 是一個JavaScript worker ,因此它不能直接訪問 DOM 。但 service worker 能夠經過postMessage 接口與跟其相關的頁面進行通訊,發送消息,從而讓這些頁面在有須要的時候去操縱 DOM 。
  • Service worker 是一個可編程的網絡代理,容許你去控制如何處理頁面的網絡請求, 能夠處理fetch請求。
  • Service worker 在不使用時將被終止,並會在須要的時候從新啓動,所以你不能把onfetch 和onmessage事件來做爲全局依賴處理程序。若是你須要持久話一些信息並在從新啓動Service worker後使用他,可使用 IndexedDBAPI ,service worker 支持。
  • Service Worker 的緩存機制是依賴 Cache API 實現的 Service worker 普遍使用了 promise。
  • Service worker依賴 HTML5 fetch API
  • Service Workers 要求必須在 HTTPS 下才能運行

Service Worker生命週期

sw-lifecycle.png

  1. 註冊service worker,在網頁上生效
  2. 安裝成功,激活 或者 安裝失敗(下次加載會嘗試從新安裝)
  3. 激活後,在sw的做用域下做用全部的頁面,首次控制sw不會生效,下次加載頁面纔會生效。
  4. sw做用頁面後,處理fetch(網絡請求)和message(頁面消息)事件 或者 被終止(節省內存)。

須要提早掌握的API

  • Cache API基本使用
  • (1)檢測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');
   });

  • Fetch API基本使用
// url (required), options (optional)
fetch('https://davidwalsh.name/some/url', {
    method: 'get'
}).then(function(response) {
     
}).catch(function(err) {
    // Error :(
});

其中options對象包含如下屬性:promise

  • method - GET, POST, PUT, DELETE, HEAD
  • url - 請求的連接
  • headers - 請求的header對象
  • referrer - 請求的referrer對象
  • mode - cors, no-cors, same-origin
  • credentials - 設置請求可不能夠攜帶cookie
  • redirect - follow, error, manual
  • integrity - 子資源完整值
  • cache - 緩存模式 (default, reload, no-cache)

 能夠在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有:緩存

  • type - basic, cors
  • url
  • useFinalURL - 上面的url參數是否是最終的URL
  • status - 狀態碼(ex: 200, 404, etc.)
  • ok - 是否成功響應 (範圍在 200-299)
  • statusText - 狀態碼 (ex: OK)
  • headers - 響應的headers對象

另外Response的實例還具有如下方法:服務器

  • clone() - 建立Response對象的克隆。
  • error() - 返回與網絡錯誤關聯的新Response對象。
  • redirect() - 使用不一樣的URL建立新響應。
  • arrayBuffer() - 返回使用ArrayBuffer解析的promise。
  • blob() - 返回使用Blob解析的promise。
  • formData() - 返回使用FormData對象解析的promise。
  • json() - 返回使用JSON對象解析的promise。
  • text() - 返回使用USVString(文本)解析的promise。
// 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);
    });

Service Worker的使用

  1. 兼容低版本,注入Cache API的一個polyfill,Service Worker須要依賴Cache API:
self.importScripts('./serviceworker-cache-polyfill.js');
  1. 註冊service worker:
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/)。

  1. 安裝service worker:
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 總會有要更新的時候。在那時,你須要按照如下步驟來更新:

  • 更新你 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);
          }
        })
      );
    })
  );
});
相關文章
相關標籤/搜索