We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

これは実験的な機能です。本番で使用する前にブラウザー実装状況をチェックしてください。

この記事はサービスワーカーを使い始めるための情報を提供するページです。基本的な構造、サービスワーカーの登録、新しいサービスワーカーのインストールと有効化のプロセス、サービスワーカーの更新、キャッシュやレスポンスの操作を含めた、オフラインで動作するシンプルなアプリケーションの機能についてです。

サービスワーカーの前提条件

ウェブユーザーが長年苦しめられてきた主要な問題の一つは、接続を失うことです。世界中の最高のウェブアプリは、ダウンロードできないときの使い勝手が最悪でした。この問題を解決するために様々な技術の開発が試みられ、私たちのオフラインページが示すように、いくつかの問題は解決済みです。しかし、資産のキャッシュとネットワークリクエストを全てコントロールする良いメカニズムはまだ存在しません。

以前の取組み - AppCache - は、キャッシュする資産をとても簡単に指定することが可能になるため、良いアイデアに見えました。しかしながら、 AppCache には利用において数多くの取り決めがあり、アプリケーションが完全に取り決めに従わない場合は壊れてしまいました。さらに詳しいことは Jake Archibald の Application Cache is a Douchebag にをお読みください。

メモ: Firefox 44 で AppCache がページのオフラインサポートを提供するために使用しようとすると、コンソールに警告メッセージが表示され、代わりにサービスワーカーを使うように開発者にアドバイスするようになりました (バグ 1204581)。

サービスワーカーは最終的にこれらの問題を解決するでしょう。サービスワーカーの構文は AppCache の構文と比べて複雑です。しかし、代わりに AppCache で動作させていたような振る舞いを素晴らしい粒度でコントロールするために JavaScript を使用することができます。あなたはこれまでの問題や、いろいろなことを処理できるようになります。サービスワーカーを使用することで、アプリケーションがはじめにキャッシュされた資産を使用するよう簡単に設定することができます。そのため、一度ネットワークからデータを取得しておけば、オフラインでも既定の機能を提供できます (オフラインファーストとして一般的に知られています)。このようなオフラインの機能はネイティブアプリでは既に使用可能であり、ネイティブアプリがウェブアプリを差し置いて選ばれる理由の一つです。

サービスワーカーを始めるための設定

多くのサービスワーカーの機能は、それらに対応しているブラウザーの最新版ではじめから利用できるようになっています。もしデモコードがあなたの環境で動作しない場合は、以下のいずれかの設定を行い、有効化してください。

  • Firefox Nightly: about:config の画面で dom.serviceWorkers.enabled を true に設定し、ブラウザーを再起動してください。
  • Chrome Canary: chrome://flags の画面で experimental-web-platform-features を有効にし、ブラウザーを再起動してください(現在いくつかの機能はChromeでははじめから有効になっています)。
  • Opera: opera://flags の画面で Support for ServiceWorker を有効にし、ブラウザーを再起動してください。
  • Microsoft Edge: about:flags の画面で Enable service workers にチェックを入れ、ブラウザーを再起動してください。

また、コードは HTTPS を通して提供する必要があります。サービスワーカーはセキュリティ上の理由から HTTPS を通して実行されるよう制限されています。そのため、 GitHub は実験を行うのによい場所です。 GitHub は HTTPS をサポートしています。

基本構造

サービスワーカーでは、セットアップの際に下記のステップが一般的に見られます。

  1. サービスワーカーの URL がフェッチされ、 serviceWorker.register() を通して登録されます。
  2. 成功した場合、サービスワーカーは ServiceWorkerGlobalScope で実行されます。これは根本的に特殊なワーカーコンテクストで、メインスレッドから独立しています。 DOM へのアクセスもありません。
  3. サービスワーカーはイベントの処理ができる状態になりました。
  4. サービスワーカーにコントロールされたページが今後アクセスされた時、ワーカーのインストールが試みられます。インストールのイベントは常に最初にサービスワーカーへ送られます(このイベントは IndexedDB の設定やサイト資産のキャッシュに使用することができます)。これはネイティブアプリや Firefox OS アプリのインストールと全く同じ種類 - 全てをオフラインで使用できるようにするための - の処理です。
  5. oninstall ハンドラーが完了すると、サービスワーカーはインストールされたと考えられます。
  6. 次のステップは有効化です。サービスワーカーがインストールされると、次に有効化イベントを受け取ります。 onactivate の基本的な使用法は、以前のバージョンのサービスワーカースクリプトで使用したリソースのクリーンアップです。
  7. 現在サービスワーカーはページを制御しているでしょう。しかし register() が成功した後に開かれたページのみです。例えば、文書はサービスワーカーがあろうとなかろうと実行され、実行されている間はその状態を保ちます。つまり、文書が実際に制御されるには、リロードされる必要があるでしょう。

以下の図は、利用可能なサービスワーカーのイベントの概要を示しています。

インストール、起動、メッセージ、フェッチ、同期、プッシュ

Promise

Promise は複数の非同期の動作を実行する優れたメカニズムです。サービスワーカーの働き方の中心となっています。

Promise は多くのすばらしいことを行うことができます。しかし今は知っておくべきことは、何かが Promise を返したとき、末尾に .then() を繋げることによって、成功時や失敗時などのコールバックをその中に含めることができ、失敗時のコールバックを含めたいときは最後に .catch() を挿入することもできるということです。

昔ながらの同期的なコールバック構造と、それに相当する非同期的な promise を比較してみましょう。

sync

try {
  var value = myFunction();
  console.log(value);
} catch(err) {
  console.log(err);
}

async

myFunction().then(function(value) {
  console.log(value);
}).catch(function(err) {
  console.log(err);
});

初めの例では、私たちはほかのコードが実行できるようになる前に、 myFunction() が実行され、 value が返るのを待たなくてはなりません。2つ目の例では、 myFunction()valuepromise を返し、残りのコードも実行に移すことができます。 promise が解決されるとき、 then の中のコードが非同期的に実行されます。

それでは実例を - 動的に画像を読み込みたい、しかし表示しようとする以前の段階で読み込みが終了していることを確実にしたいときはどうするのでしょう。これは私たちがしたい標準的なことですが、少しの痛みとなることもあります。読み込まれた後に表示するためだけなら私たちは .onload を使用することができます。しかし私たちがリッスンするより前に起こり始めたイベントはどうなるのでしょう。私たちは .complete を使うこともできたでしょう。しかしそれはまだfoolproofではありません。複数の画像のときはどうするのでしょう。そしてそれは同期的に行われるため、メインスレッドをブロックしてしまいます。

代わりに、私たちはこのようなケースを操作するための独自の promise を作成することができます(私たちの Promise テストの例のソースコードを見てください。このコードが無事動いていることにも注目してください)。

註: 実際のサービスワーカーの導入では非推奨の XMLHttpRequest API よりもキャッシュと onfetch が使われるでしょう。 Promise の理解に集中できるようにそれらの機能はここでは使用されていません。

function imgLoad(url) {
  return new Promise(function(resolve, reject) {      
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';

    request.onload = function() {
      if (request.status == 200) {
        resolve(request.response);
      } else {
        reject(Error('Image didn\'t load successfully; error code:' + request.statusText));
      }
    };

    request.onerror = function() {
      reject(Error('There was a network error.'));
    };

    request.send();
  });
}

私たちは resolvereject を引数に持つコールバック関数を引数にとる Promise() コンストラクタを使用して新しい promise を返します。この関数のどこかで、何が起きた時に成功とするか、また何が起きた時に失敗とするか - このケースでは 200 OK のステータスが返るかどうか - を定義し、成功時には resolve を、失敗時には reject を呼ぶ必要があります。関数内の残りの要素は、標準的な XHR のものですので、今は気にする必要はありません。

imgLoad() を呼ぶときは、私たちが期待している通り、読み込みたい画像への URL を一緒に呼ばなくてはなりません。しかし残りのコードは、少し異なります。

var body = document.querySelector('body');
var myImage = new Image();

imgLoad('myLittleVader.jpg').then(function(response) {
  var imageURL = window.URL.createObjectURL(response);
  myImage.src = imageURL;
  body.appendChild(myImage);
}, function(Error) {
  console.log(Error);
});

関数呼び出しの末尾に、二つの関数を持つ then() メソッドをつなげています。一つ目の関数は promise が成功として resolve されたときに実行されます。二つ目の関数は promise が失敗したときに呼び出されます。成功のケースでは、 myImage の中で画像を表示して、 body に付け加えています (この引数は promise の中の resolve メソッドに含まれる request.response です)。失敗のケースではコンソールにエラーを返しています。

これらはすべて非同期的に起こります。

メモ: 次のように promise の呼び出しを互いにつなげていくこともできます。
myPromise().then(success, failure).then(success).catch(failure);

メモ: Jake Archibald の素晴らしい JavaScript Promises: there and back again を読むことで promise についてのより多くの発見をすることができます。

サービスワーカーのデモ

サービスワーカーの登録とインストールについてのとても基本的なデモンストレーションをするために、私たちは sw-test という、スターウォーズのレゴの画像を表示するシンプルなデモを作成しました。このデモでは、 JSON オブジェクトから画像データを取得し、 Ajax を使用してページの下の行にある画像が表示される前に画像を読み込むために、 Promise を活用した関数を使用しています。私たちは今のところ物事を固定的でシンプルに保っています。デモはまたサービスワーカーを登録し、インストールし、有効化します。そしてブラウザーがサポートしているときは必要なすべてのファイルをキャッシュし、オフラインで動作するでしょう。

GitHub でソースコードを見ることができます。またサンプルの動きも見ることができます。私たちがここで強調したいことの一つは Promise です (app.js の17-42行目を見てください)。上のほうで見た Promise のテストデモのコードを修正したものです。下記の部分が異なっています。

  1. オリジナルでは、ほしい画像を読み込むための URL を渡しているだけですが、今回のバージョンでは、一つの画像の全ての情報を含む JSON を渡しています(JSON がどのような形かは image-list.js を見てください)。これはそれぞれの Promise が resolve するすべてのデータが、非同期で動作するためには、 Promise とともに渡されなければならないためです。もし URL のみを渡し、 for() ループの中で後から JSON に含まれるほかの要素にアクセスしようとすると、 Promise が for() ループの進行と同時に resolve を返さないため、うまく動作しないでしょう (これは同期的な処理になります)。
  2. 配列とともに Promise は resolve されます。 resolve 後の関数で読み込まれた画像の Blob、画像名、クレジットや alt テキストを利用可能にするためです (app.js の26-29行目を見てください)。 Promise は一つの引数のみで resolve するので、もし複数の値とともに resolve したい場合は、配列かオブジェクトを使用する必要があります。
  3. resolve された Promise の値にアクセスするためには、 then が期待する関数を使用します (app.js の55-59行目を見てください)。初めは少し奇妙に見えるでしょうが、これが Promise の動き方です。

サービスワーカーに入る

サービスワーカーを取得してみよう!

workerを登録する

私たちのアプリのJavaScriptファイル— app.js —の最初のブロックは次の通りです。これは サービスワーカーを使用するエントリーポイントです。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw-test/sw.js', {scope: '/sw-test/'})
  .then(function(reg) {
    // registration worked
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
}
  1. 外側のブロックは、登録を試みる前にサービスワーカーがサポートされているか確認する機能検出試験を行います。
  2. 次に、アプリの内部にあるJavaScriptファイルをこのサイトのservice wokerに登録するために、ServiceWorkerContainer.register()関数を使用します(これはオリジンへのファイルの相対URLであって、JSファイルの参照ではないことに注意してください)。
  3. scopeパラメータはオプションで、サービスワーカーが制御するコンテンツのサブセットを指定するために使用できます。このケースでは、'/sw-test/' を指定しており、アプリのオリジン配下のすべてのコンテンツを意味しています。もしこれを指定しなくてもこの値が既定値になりますが、指定方法を示すために使用しています。
  4. promiseの構造に成功時の処理をチェーンするために、.then() promise関数を使用しています。promiseが成功裡に解決されたとき、内部のコードが実行されます。
  5. 最後にpromiseが拒否されたときに実行される.catch()関数をチェーンします。

これは、workerコンテクストで実行されるサービスワーカーを登録しているため、DOMアクセスは含まれていません。続いて、通常ページの外側のサービスワーカーでロードを制御するためにコードを実行します。

1つのサービスワーカーで、複数のページを制御できます。スコープ内のページがロードされるたび、サービスワーカーはそのページにインストールされて動作します。そのため、サービスワーカースクリプト内でのグローバル変数の扱いには注意が必要だということを心にとどめておいてください。各ページは独自のworkerを持ちません。

メモ: プロキシサーバーのように、サービスワーカー関数はリクエストとレスポンスをキャッシュなどからのアイテムに置き換えるように修正することができます。

メモ: サービスワーカーのすばらしいところは、上述のような機能検出をした場合、サービスワーカーをサポートしないブラウザーは通常予想される流儀通りにオンラインでアプリを使用できます。さらに、1つのページで AppCacheとSWを使用した場合、SWはサポートしていないがAppCacheはサポートしているブラウザーはAppCacheを使い、両方サポートしているブラウザーはAppCacheを無視してSWを採用します。

なぜサービスワーカーの登録に失敗するのか?

次の可能性が考えられます:

  1. HTTPS通信を使用してアプリケーションを実行していない。
  2. サービスワーカーファイルへのパスが正しく書かれていない— アプリのルートディレクトリからではなく、オリジンからの相対パスが書かれている必要があります。例えば、workerが https://mdn.github.io/sw-test/sw.jsにあり、アプリのルートがhttps://mdn.github.io/sw-test/だとします。このとき、パスは/sw-test/sw.js だとします。このとき、パスは/sw-test/sw.jsと書かれるべきで/sw.jsではありません。
  3. サービスワーカーの指定先があなたのアプリと異なるオリジンである。これは許可されていません。

他のメモです。

  • サービスワーカーはサービスワーカーのスコープ内にあるクライアントからのリクエストのみを取得します。
  • サービスワーカーの最大スコープは、ワーカーのある場所です。
  • サービスワーカーが Service-Worker-Allowed ヘッダーから提供されたクライアント上で起動する場合、そのワーカーの最大スコープのリストを指定することができます。
  • Firefox では、サービスワーカー API はユーザーがプライベートブラウジングモードにいるときは、隠され使用することができません。

インストールと起動: キャッシュを配置する

サービスワーカーを登録した後、ブラウザーはページ/サイト上でサービスワーカーをインストールおよび起動しようとします。

インストールが成功裡に完了したとき、installイベントが発火されます。通常、installイベントはオフラインで実行する必要のある資産を、ブラウザーのオフラインキャッシュ領域に配置するために使われます。これをするために、サービスワーカーの真新しいstorage API— cache — を使用します。cacheはサービスワーカー上でグローバルに、レスポンスで提供された資産を保存したり、リクエストにキーしたりできます。 このAPIはブラウザーの標準キャッシュと同じようにふるまいますが、ドメインに特化しています。キャッシュが不要であることを再度伝えるまで存在します。つまり、すべてを制御できます。

メモ: Cache APIはすべてのブラウザーでサポートされているわけではありません (詳細はBrowser support セクションを見てください)。今すぐこれを使いたいなら、Google's Topeka demoで利用されているポリフィルの使用か、IndexedDBに資産を格納することを検討してください。

コードサンプルを見ることからこのセクションを始めましょう。―これは私たちのサービスワーカーで最初に見つかるブロックです

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});
  1. ここではサービスワーカー(thisによって)にイベントリスナーを追加して、イベントにExtendableEvent.waitUntil() メソッドをチェーンしています―これはwaitUntil()の内部のコードが成功裡に実行されるまで、サービスワーカーがインストールされないことを保証します。
  2. waitUntil()内で、サイトリソースキャッシュのバージョン1であるv1と呼ばれる新しいキャッシュを生成するために、Caches.open()メソッドを使用します。これは生成されたキャッシュのためにpromiseを返します。ひとたび解決されたら、パラメータとしてキャッシュしたいすべてのリソースのオリジン相対URLの配列を受け取る、生成されたキャッシュのaddAll()を呼び出す関数をコールします。
  3. promiseが拒否された場合やインストールが失敗した場合、workerは何もしません。コードを修正して再度登録にトライすればOKです。
  4. インストールが成功した後、サービスワーカーがアクティベートします。最初にサービスワーカーがインストール/アクティベートされたときには独特な使い方はありませんが、サービスワーカーを更新したときにより意義があります(後に続くUpdating your service worker を見てください)。

メモ: localStorageはサービスワーカーキャッシュと同じように動作しますが、同期処理のため、サービスワーカー内では許可されていません。

メモ: 必要ならば、IndexedDBをサービスワーカー内でデータ保存のために使用できます。

リクエストに対するカスタムレスポンス

サイトの資産はキャッシュされたので、サービスワーカーにキャッシュされたコンテンツを使って何かするように指示する必要があります。これはfetchイベントを使って簡単に実現できます。

fetch イベントが発火するたび、指定されたスコープ内の文書とこれらの文書が参照するすべてのリソースを含む、サービスワーカーによって制御されたリソースの取得します(例えば、index.html が画像を埋め込むためにクロスオリジンリクエストすると、それはサービスワーカーを通過します)。

イベントリスナーをサービスワーカーにアタッチしてから、HTTPレスポンスをハイジャックしてマジックを使って更新するために、イベント上でrespondWith() メソッドを呼び出せます。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    // magic goes here
  );
});

ネットワークリクエストしたURLとマッチするリソースを単純にレスポンスすることから始められます。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
  );
});

使用可能なキャッシュと一致する場合、 caches.match(event.request) がキャッシュで利用可能な同等のリソースをネットワークから要求された各リソースに一致するようにできます。マッチングは通常のHTTPリクエストと同様に、URLを経由して行われ、ヘッダを変更します。

マジックを定義するときの、いくつかのオプションを見てみよう(RequestオブジェクトとResponseオブジェクトの詳細については、Fetch API documentationを見て下さい)。

  1. Response() コンストラクタを使用し、任意のレスポンスを作成できます。この例では、単純なテキストデータが返却されます。

    new Response('Hello from your friendly neighbourhood service worker!');
  2. このより複雑な例では、一般的なHTTPレスポンスを再現するためにヘッダ情報をレスポンスに付与できることを示しています。この例では、レスポンスの種類のみをブラウザーに通知しています。

    new Response('<p>Hello from your friendly neighbourhood service worker!</p>', {
      headers: { 'Content-Type': 'text/html' }
    });
  3. もしキャッシュにリソースがなかった場合は、リソースを取得する一般的なリクエストであるfetchで、使用可能なネットワークから新たにリソースを取得できます。

    fetch(event.request);
  4. もしキャッシュにリソースがなかったり、ネットワークが使用不可能だった場合に、match()を使用し、リクエストに対して代替用のページを用意できます。

    caches.match('/fallback.html');
  5. FetchEventにより、返却されるRequestオブジェクトのプロパティから様々なリクエストの情報を取得できます。

    event.request.url
    event.request.method
    event.request.headers
    event.request.body

失敗したリクエストを復旧する

サービスワーカー cacheに一致する場合、caches.match(event.request) は優れていますが、マッチしない場合はどうでしょう?失敗時のハンドリングを何も提供しなければ、promiseはrejectして、 マッチしないときにはネットワークエラーが発生します。

幸運なことに、サービスワーカーのpromiseベースの構造は、成功に向けたさらなるオプションを設定するためにささやかなことができます。具体的には、以下のようにできます。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

リソースがキャッシュになかった場合は、ネットワークからリクエストされます。

私たちが本当に賢いなら、リソースをネットワークから要求するだけではないはずです。後のリクエストがオフラインでもリソースを扱えるように、キャッシュにもセーブするでしょう。これは、余分な画像がStar Warsギャラリーに追加されたなら、アプリが自動的に画像をとらえてキャッシュすることを意味します。 以下の例はトリックを行っています。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(resp) {
      return resp || fetch(event.request).then(function(response) {
        return caches.open('v1').then(function(cache) {
          cache.put(event.request, response.clone());
          return response;
        });  
      });
    })
  );
});

ここでは、promiseを返すfetch(event.request)と共に、既定のネットワークリクエストを返しています。このpromiseが拒否されたとき、caches.open('v1')を使ってキャッシュをとらえる関数を実行して応答します。これもまた、promiseを返します。promiseが拒否されたとき、cache.put() がキャッシュにリソースを追加するために使われます。リソースはevent.requestから捕捉されてから、レスポンスはresponse.clone() でクローンされ、キャッシュに加えられます。クローンはキャッシュに挿入され、オリジナルのリクエストは呼び出したページに与えるためにブラウザーに返されます。

リクエスト及びレスポンスストリームは一度しか読み出せないので、レスポンスの複製を作る必要があります。ブラウザーへレスポンスを返し、かつキャッシュに入れるために、クローンを作成する必要があるのです。ですから、オリジナルはブラウザーに返され、クローンはキャッシュに送られます。それぞれ一度ずつ読み出されます。

これで私たちの抱えるトラブルは、リクエストがいずれのキャッシュともマッチせず、ネットワークが使用できない場合、いまだリクエストが失敗することだけになりました。何が起こってもユーザーが少なくとも何かは取得できるように、既定の代替処理を提供しましょう。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(resp) {
      return resp || fetch(event.request).then(function(response) {
        let responseClone = response.clone();
        caches.open('v1').then(function(cache) {
          cache.put(event.request, responseClone);
        });

        return response;
      });
    }).catch(function() {
      return caches.match('/sw-test/gallery/myLittleVader.jpg');
    })
  );
});

失敗の可能性があるのは新しい画像の更新だけのための代替画像を選択しました。以前確認した、installイベントリスナでのインストールに依存している他の画像でも同じことができます。

サービスワーカーの更新

サービスワーカーが以前にインストールされているが、ページの更新や読み込みの時に新しいバージョンのワーカーが利用できる場合、新しいバージョンはバックグラウンドでインストールされますが、まだ起動しません。まだ古いサービスワーカーを使用している読み込まれたページがなくなったら、新しいサービスワーカーが起動します。

新しいサービスワーカーで、 install イベントリスナーをこのようなもの (新しいバージョン番号を通知するもの) に更新したくなるでしょう。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v2').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        
        …

        // 新しいバージョンのための新しい他のリソースを入れてください...
      ]);
    })
  );
});

これが発生している間、以前のバージョンはまだfetchに対して応答します。新しいバージョンは、バックグラウンドでインストールされています。以前の v1 キャッシュを妨げないように、新しいキャッシュを v2 と呼んでいます。

どのページも現在のバージョンを使用していないとき、新しいworkerは有効化し、fetchに応答するようになります。

古いキャッシュの削除

あなたはactivateイベントも取得できます。これは一般的に、実行中の以前のバージョンを破壊するために使われます。たとえば、古いキャッシュを取り除きます。これはディスクスペースがいっぱいになることを防ぐために、最早不要なデータを削除するのにも役立ちます — それぞれのブラウザーはサービスワーカーが使うために与えられたキャッシュストレージの容量に対して厳しい制限があります。ブラウザーはディスクスペースを管理するために最善を尽くしていますが、オリジンのためのキャッシュストレージを削除するかもしれません。 一般的にブラウザーはオリジンのためのデータをすべて削除するか、オリジンのためのデータを持っていません。

waitUntil()に渡されたPromiseは、完了するまで他のイベントをブロックするので、新しいキャッシュ上で初めてのfetchイベントを取得するときには、クリンナップ操作が完了していると確信できます。

self.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

開発ツール

Chromeには、現在サービスワーカーのアクティビティやデバイス上のストレージを示すchrome://inspect/#service-workersがあります。また、より詳細な情報の表示やworker processを開始/停止/デバックできるchrome://serviceworker-internalsがあります。将来的には、不良または存在しない接続をエミュレートするスロットリング/オフラインモードを実装する予定です。これは本当に素晴らしい機能です。

Firefoxもサービスワーカーに関する便利なツールを実装し始めています。

  • サービスワーカーで何が登録されているかやそれらが更新/削除されているかを見るために、about:serviceworkersにナビゲートできます。
  • テストを行う場合、Firefox開発ツールオプション内のオプション(ギアメニュー)で、"HTTPによるサービスワーカーを有効(ツールボックスを開いたとき)"をチェックすることで、HTTPS 制約を回避できます。

メモ: ローカルでの開発のために、 http://localhost (例えば using me@localhost:/my/app$ python -m SimpleHTTPServer) からアプリのサービスを行うことができます。 Security considerations を参照してください。

仕様策定状況

仕様書 策定状況 コメント
Service Workers 草案 初回定義。

ブラウザーの対応

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

機能 Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
基本対応 40.0 16[2] 33.0 (33.0)[1] 未サポート 24 未サポート
機能 Android Webview Chrome for Android Firefox Mobile (Gecko) Firefox OS IE Phone Opera Mobile Safari Mobile
基本対応 未サポート 40.0 (有) (有) 未サポート (有) 未サポート

[1] サービスワーカー (及び Push) は Firefox 45 Extended Support Release (ESR.) では無効になりました。

[2] Microsoft Edge の試験的な対応が EdgeHTML 16 でフラグに隠されて行なわれています。

関連情報

ドキュメントのタグと貢献者

最終更新者: mfuji09,