サービス・ワーカーを作る。 ケーススタディ

著者について

Lyza Danger Gardnerは、開発者です。 2007年にオレゴン州ポートランドを拠点とするモバイルウェブスタートアップのCloud Fourを共同設立して以来、彼女は、モバイルアプリケーションの開発で自分自身を苦しめ、スリリングにさせてきました…More aboutLyza↬

  • 23 min read
  • Coding,JavaScript,Techniques,Service Workers
  • Saved for offline reading
  • Share on Twitter.com
  • コーディング、JavaScript、テクニック、サービスワーカー。 LinkedIn
サービスワーカーとは何か、登録・インストール・有効化で面倒なく自分で組み立てる方法について解説しています。

初期のサービス ワーカー API は、現在いくつかの一般的なブラウザで出荷されており、その宣伝や興奮には事欠かないでしょう。 クックブックやブログ投稿、コード スニペット、ツールなどがあります。 しかし、新しい Web のコンセプトを徹底的に学びたい場合、袖をまくり上げ、飛び込んで、ゼロから何かを構築することが理想的であることがよくあります。

今回遭遇した衝突や傷、不具合やバグには、利点があります。 今、私はサービス ワーカーをよりよく理解しており、運が良ければ、私が新しい API で作業するときに遭遇した頭痛の種を避ける手助けができるかもしれません。 つまり、

  • Web サイトをオフラインで機能させる、
  • 特定のアセットに対するネットワーク要求を減らすことによりオンライン パフォーマンスを向上させる、
  • オフライン フォールバック体験をカスタマイズして提供する、といった機能です。

始める前に、これを可能にした 2 人の人物に謝意を表したいと思います。 まず、私自身のコードの出発点となった、彼自身の Web サイトでのサービス ワーカーの実装について、Jeremy Keith に多大な恩義を感じています。 彼の最近の投稿に触発され、現在進行中のサービスワーカーの経験を記述しました。 実際、私の仕事は非常に大きく派生しており、以前の投稿での Jeremy の励ましがなければ、それについて書くことはなかったでしょう。

So if you decide to play around with Service Workers, please, please share your experience.

Secondly, all sorts of big old thanks to Jake Archibald for his excellent technical review and feedback.

サービス ワーカー仕様の作成者および伝道者の 1 人が、あなたを正してくれるのは嬉しいことです!

What Is A Service Worker?

サービス ワーカーは、Web サイトとネットワークの間に立って、特に、ネットワーク要求を傍受してさまざまな方法でそれに対応する能力を与えてくれるスクリプトです。 以前は、この管理は主にブラウザの特権でした。 もしブラウザがネットワークにアクセスできなければ、「オフラインです」というメッセージが表示されるはずです。 資産のローカル キャッシュを促進するために使用できるテクニックはありましたが、多くの場合、ブラウザが最終決定権を持っていました。

これはオフラインのユーザーにとってあまり良い経験ではなく、Web 開発者はブラウザ キャッシュをほとんど制御できないままでした。 表向きは、Web サイトやアプリがオフラインで動作するように、異なるアセットをどのように処理すべきかを指示することができます。 しかし、AppCache のシンプルに見える構文は、その根本的な不可解な性質と柔軟性の欠如を裏付けていました。

初期のサービス ワーカー API は、AppCache が行ったこと、さらに多くのことを行うことができます。 しかし、最初は少し大変に見えます。 仕様は重く、抽象的な読み物であり、多数の API がそれに従属するか、さもなければ関連しています。 cachefetch などです。 サービスワーカーは多くの機能を包含しています。プッシュ通知や、まもなくバックグラウンドでの同期も可能になります。 AppCache と比較すると、それは…複雑に見えます。

AppCache (ところで、これはなくなりつつあります) は学ぶのは簡単ですが、その後のすべての瞬間がひどい (私の意見) のに対して、サービス ワーカーは初期の認識投資がより多く必要ですが、強力で便利で、物事を破壊しても概して問題から脱出することができます。 そのファイルでは、あなたが知っていて好きなように JavaScript を書くことができますが、心に留めておくべきいくつかの重要なことがあります。

サービス ワーカー スクリプトは、制御するページとは別のブラウザのスレッドで実行されます。 ワーカーとページの間で通信する方法はありますが、ワーカーは別のスコープで実行されます。 つまり、たとえば、それらのページの DOM にアクセスすることはできません。 これはまったく正確ではありませんが、自分自身を混乱から守るための大まかな比喩として役立ちます。 非同期 API を使用する必要があります。 たとえば、サービス ワーカーでは localStorage を使用できません (localStorage は同期 API です)。 おかしなことに、このことを知っていても、後ほど説明するように、私は違反する危険を冒すことができました。

Registering a Service Worker

サービス ワーカーを有効にするには、登録します。 この登録は、サービスワーカーの外部から、Web サイトの別のページまたはスクリプトによって行われます。 私の Web サイトでは、グローバル site.js スクリプトがすべての HTML ページに含まれています。 そこからサービスワーカーを登録します。

サービスワーカーを登録するとき、(オプションで)どのスコープに適用すべきかも指示します。 サービスワーカーに Web サイトの一部 (たとえば '/blog/') のみを処理するように指示することも、私のように Web サイト全体 ('/') に対して登録することもできます。

Service Worker Lifecycle And Events

A service worker does the bulk of its work by listening for relevant events and responding them in useful ways.A service worker は、関連イベントをリッスンし、それに対し有用な方法で対応します。 サービス ワーカーが登録され、ダウンロードされると、バックグラウンドでインストールされます。 サービス ワーカーは install イベントをリッスンし、この段階に適したタスクを実行できます。

このケースでは、install 状態を利用して、後でオフラインで利用可能にすることが分かっている資産の束を事前にキャッシュしたいと思います。 これは、サービス ワーカーがその scope 内で物事をコントロールできるようになったことを意味し、その役割を果たすことができます。 activate イベントは、新しいサービス ワーカーにとってはあまり刺激的ではありませんが、サービス ワーカーを新しいバージョンで更新するときに、どのように役に立つかを見ていきます。

一度インストールとアクティブ化が完了すると、サービス ワーカーの更新バージョンがダウンロードされ登録されるまで、再びアクティブ化は行われません。 たとえば、同期イベントや通知イベントなどです。

追加単位または余暇の楽しみとして、サービスワーカーが実装するインターフェイスについてもっと読むことができます。

The Service Worker’s Promise-Based API

The service worker API makes heavy use of Promises.This is by implementing these interfaces that service workers get the bulk of their events and their much of extended functionality.

getAnAnswerToADifficultQuestionSomewhereFarAway() .then(answer => { console.log('I got the ${answer}!'); }) .catch(reason => { console.log('I tried to figure it out but couldn't because ${reason}');});

getAnAnswer… 関数は Promise を返し、それは最終的に私たちが探している answer によって満たされる、または解決されることを (私たちは) 望んでいます。 その後、その answer は連鎖した then ハンドラ関数に渡すことができます。または、残念ながら目的を達成できなかった場合、Promise は拒否され、catch ハンドラ関数はこれらの状況を処理することができます。

注意: サービス ワーカーのサンプル コードで特定の ECMAScript6 (または ES2015) 機能を使用していますが、これはサービス ワーカーをサポートするブラウザがこれらの機能もサポートしているためです。 具体的には、矢印関数とテンプレート文字列を使用しています。

Other Service Worker Necessities

また、サービス ワーカーは動作するために HTTPS を必要とすることに留意してください。 このルールには、重要で便利な例外があります。 サービス ワーカーは安全でない http 上で localhost のために動作し、ローカル SSL を設定するのが面倒な場合があるので、これは救いです。 これは、将来ブラウザに登場する楽しい新機能のほとんどすべてが SSL の使用を必要とするため、皆さんに検討していただきたいことです。

これからまとめるものはすべて、今日 Chrome で動作します (私はバージョン 47 を使用しています)。 今にも Firefox 44 が出荷され、サービス ワーカーをサポートします。 Is Service Worker Ready? では、さまざまなブラウザでのサポートに関する詳細な情報を提供しています。

Registering, Installing And Activating A Service Worker

さて、いくつかの理論に対処したところで、サービス ワーカーの組み立てを開始しましょう。

サービス ワーカーをインストールしてアクティブにするには、installactivate のイベントをリッスンして、それらに対応します。 serviceWorker.js:

self.addEventListener('install', event => { // Do install stuff});self.addEventListener('activate', event => { // Do activate stuff: This will come later on.});

Registering Our Service Worker

ここで、サービス ワーカーを使用するように Web サイトのページに指示する必要があります。

私の site.js:

if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js', { scope: '/' });}

Pre-Caching Static Assets During Install

私はインストール段階を使って、Web サイトのいくつかの資産を事前にキャッシュしたいと思います。

  • Web サイトの多くのページで使用されるいくつかの静的アセット (画像、CSS、JavaScript) を事前にキャッシュすることにより、その後のページ読み込み時にネットワークから取得する代わりに、キャッシュからこれらを取得して読み込み時間を短縮することができます。

これを行う手順は次のとおりです。

  1. installイベントを待機させ、event.waitUntil を使用して必要なことを完了するまで完了しないようにします。 プログレッシブ Web アプリの用語では、これらの資産は「アプリケーション シェル」を構成します。

/serviceWorker.js では、install ハンドラーを展開します。 caches にはいくつかの便利なメソッドがあります。たとえば opendelete です。

ここで、Promises が動作しているのを見ることができるでしょう。 caches.openstatic キャッシュを正常に開くと、cache オブジェクトに解決する Promise を返します。addAll はまた、渡されたすべてのアイテムがキャッシュに格納されると解決する Promise を返します。

私は event に、私のハンドラー関数が返す Promise が正常に解決するまで待つように言っています。 そうすれば、インストールが完了する前に、それらの事前キャッシュ項目のすべてがソートされることを確認できます。

コンソールの混乱

Stale Logging

おそらくバグではありませんが、確かに混乱があります。 サービス ワーカーから console.log を実行すると、Chrome は、後続のページ要求でそれらのログ メッセージをクリアするのではなく、再表示しつづけます。

たとえば、install ハンドラーに log ステートメントを追加してみましょう。

self.addEventListener('install', event => { // … as before console.log('installing');});
Chrome 47 では、「インストールしている」ログ メッセージがその後のページ要求で引き続き表示されるようになります。 Chrome は実際には、すべてのページ読み込みで install イベントを発生させているわけではありません。 その代わりに、古いログが表示されます。 (拡大図を表示)

正常な場合のエラー

もう 1 つの奇妙なことは、サービス ワーカーがインストールされて有効になると、その範囲内の任意のページに対するその後のページ読み込みで、常にコンソールに 1 つのエラーが発生することです。 何か間違ったことをしているのかと思いました。

Chrome 47 では、すでに登録されたサービス ワーカーを持つページにアクセスすると、常にこのエラーがコンソールに表示されるようになりました。 (拡大図を表示)

ここまでで達成したこと

サービス ワーカーは install イベントを処理し、いくつかの静的アセットを事前にキャッシュしています。

serviceWorker.js の内容は GitHub にあります。

Fetch Handling With Service Workers

これまでのところ、このサービス ワーカーには具体的な install ハンドラがありますが、それ以上のことは何もしていません。 サービス ワーカーの魔法は、fetch イベントがトリガーされたときに、本当に起こるでしょう。 異なるネットワーク戦略を使用することにより、常にネットワークから特定のアセットを取得しようとするようブラウザに指示することができ (重要なコンテンツが新鮮であることを確認します)、一方で静的アセットにはキャッシュされたコピーを優先させ、ページ ペイロードを非常にスリムにすることができます。

ブラウザがこのサービス ワーカーのスコープ内のアセットを取得しようとするときはいつでも、serviceWorker.js:

self.addEventListener('fetch', event => { // … Perhaps respond to this fetch in a useful way?});

eventListener を追加することにより、それを通知することができます。 これらのフェッチに対するブラウザの応答を選択的に処理することができます。

ある資産に対して fetch イベントが発生したとき、最初に判断したいことは、このサービス ワーカーが与えられたリソースの取得に干渉すべきかどうかです。

最終的に、serviceWorker.js:

self.addEventListener('fetch', event => { function shouldHandleFetch (event, opts) { // Should we handle this fetch? } function onFetch (event, opts) { // … TBD: Respond to the fetch } if (shouldHandleFetch(event, config)) { onFetch(event, config); }});

関数は与えられたリクエストを評価し、応答を提供すべきか、ブラウザにデフォルトの処理をさせるべきかを決定します。

Why Not Use Promises?

サービス ワーカーのプロミスへの偏愛を維持するために、私の fetch イベント ハンドラの最初のバージョンは次のようなものでした:

self.addEventListener('fetch', event => { function shouldHandleFetch (event, opts) { } function onFetch (event, opts) { } shouldHandleFetch(event, config) .then(onFetch(event, config)) .catch(…);});

論理的には見えますが、私はプロミスでいくつかの新人の間違いを犯していました。 私は当初からコードの匂いを感じていたのですが、Jakeが私のやり方の誤りを正してくれたのです。 (教訓: いつものように、コードが間違っていると感じたら、それはおそらくそうです。)

Promise rejections は、「気に入らない答えが返ってきた」ことを示すために使用するべきではありません。 代わりに、拒絶は、”ああ、くそ、答えを得るために何かが間違っていた” を示すべきです。

Criteria for Valid Requests

Right, back to determining whether a given fetch request is applicable for my service worker. 私のサイト固有の基準は次のとおりです。

  1. 要求された URL は、キャッシュまたは応答したいものを表す必要があります。
  2. リクエストの HTTP メソッドは GET でなければなりません。
  3. リクエストは、私のオリジン (lyza.com) からのリソースでなければなりません。 serviceWorker.js:
    function shouldHandleFetch (event, opts) { var request = event.request; var url = new URL(request.url); var criteria = { matchesPathPattern: !!(opts.cachePathPattern.exec(url.pathname), isGETRequest : request.method === 'GET', isFromMyOrigin : url.origin === self.location.origin }; // Create a new array with just the keys from criteria that have // failing (i.e. false) values. var failingCriteria = Object.keys(criteria) .filter(criteriaKey => !criteria); // If that failing array has any length, one or more tests failed. return !failingCriteria.length;}

    もちろん、ここでの基準は私自身のものであり、サイトによって異なるでしょう。 event.requestRequest オブジェクトで、フェッチ ハンドラーにどのように動作させたいかを評価するために見ることができるあらゆる種類のデータを持っています。

    Trivial note: ハンドラー関数に opts として渡される config が出現したことに気づいたら、それはよくわかりました。 再利用可能な config のような値を除外し、サービス ワーカーのトップレベル スコープに config オブジェクトを作成しました。

    var config = { staticCacheItems: , cachePathPattern: /^\/(?:(20{2}|about|blog|css|images|js)\/(.+)?)?$/};

    Why Whitelist?

    なぜこの正規表現に一致するパスのみをキャッシュするのかと疑問に思うかもしれません:

    /^\/(?:(20{2}|about|blog|css|images|js)\/(.+)?)?$/

    … いくつかの理由:

    • サービス ワーカー自体をキャッシュしたくない。
    • ウェブサイトをローカルに開発しているとき、キャッシュしたくないものがリクエストされることがあります。 たとえば、私は browserSync を使用していますが、これは私の開発環境で関連する要求の束をキックオフします。 そんなものキャッシュしたくないよ!」と。 キャッシュしたくないものをすべて考えようとすると、面倒で大変なことになります(言うまでもなく、サービスワーカーの設定にそれを明記しなければならないのは少し変です)。

    Fetch ハンドラを書く

    さて、該当する fetch リクエストをハンドラに渡す準備ができています。 onFetch 関数は、

    1. どのようなリソースが要求されているか、
    2. この要求をどのように満たすべきかを決定する必要があります。 要求されているリソースの種類は何か。

      HTTP Accept ヘッダーを見て、要求されているアセットの種類についてのヒントを得ることができます。

      function onFetch (event, opts) { var request = event.request; var acceptHeader = request.headers.get('Accept'); var resourceType = 'static'; var cacheKey; if (acceptHeader.indexOf('text/html') !== -1) { resourceType = 'content'; } else if (acceptHeader.indexOf('image') !== -1) { resourceType = 'image'; } // {String} cacheKey = resourceType; // … now do something}

      整理された状態を保つために、異なる種類のリソースを異なるキャッシュに格納したいと思います。 これにより、後でこれらのキャッシュを管理できるようになります。 キャッシュ API には意見がありません。

      2. Fetch

      に応答する 次に onFetch が行うことは、fetch イベントをインテリジェントな ResponserespondTo 処理することです。

      function onFetch (event, opts) { // 1. Determine what kind of asset this is… (above). if (resourceType === 'content') { // Use a network-first strategy. event.respondWith( fetch(request) .then(response => addToCache(cacheKey, request, response)) .catch(() => fetchFromCache(event)) .catch(() => offlineResponse(opts)) ); } else { // Use a cache-first strategy. event.respondWith( fetchFromCache(event) .catch(() => fetch(request)) .then(response => addToCache(cacheKey, request, response)) .catch(() => offlineResponse(resourceType, opts)) ); }}

      Careful With Async!

      このケースでは、shouldHandleFetchは非同期なことは何もしていないし、onFetchevent.respondWithの時点まではそうしていない。 それ以前に非同期的なことが起こっていたら大変なことになる。 event.respondWithfetchイベントが発生してから制御がブラウザに戻るまでの間に呼ばれなければならない。 event.waitUntilも同様である。 基本的に、イベントを処理する場合、すぐに(同期的に)何かを行うか、非同期的なものが完了するまで待機するようにブラウザに指示します。 ネットワーク優先の戦略の実装

      fetch リクエストに応答するには、適切なネットワーク戦略を実装する必要があります。 HTML コンテンツ (resourceType === 'content') のリクエストに応答する方法を詳しく見てみましょう。

      if (resourceType === 'content') { // Respond with a network-first strategy. event.respondWith( fetch(request) .then(response => addToCache(cacheKey, request, response)) .catch(() => fetchFromCache(event)) .catch(() => offlineResponse(opts)) );}

      ここで、コンテンツのリクエストを満たす方法は、ネットワーク優先戦略です。 HTML コンテンツは私の Web サイトの中核的な関心事であり、頻繁に変更されるので、常にネットワークから新鮮な HTML ドキュメントを取得するようにしています。

      では、この手順を説明します。 ネットワークから取得してみる

      fetch(request) .then(response => addToCache(cacheKey, request, response))

      ネットワーク リクエストが成功したら (すなわち、約束が解決したら)、先に進み、適切なキャッシュ (content) に HTML ドキュメントのコピーをため込みます。

      function addToCache (cacheKey, request, response) { if (response.ok) { var copy = response.clone(); caches.open(cacheKey).then( cache => { cache.put(request, copy); }); return response; }}

      Responses may be only once.

      We need to do two things with the response we have:

      • cache it,
      • respond to the event with it (i’s it return).

      But Response objects may be only once.This uses a read-through Caching.

      We are not do two things with the response with the response with the response with the event is used. クローンを作成することにより、キャッシュの使用のためのコピーを作成することができます。

      var copy = response.clone();

      悪い応答をキャッシュしないでください。 私がしたのと同じ間違いをしないでください。 私のコードの最初のバージョンはこの条件を持っていなかった:

      if (response.ok)

      キャッシュに 404 やその他の悪い応答が残ってしまうのはかなりすごいことです! ハッピーなレスポンスだけをキャッシュします。

      2. キャッシュからの取得を試みる

      ネットワークから資産を取得することに成功したら、完了です。 しかし、成功しなかった場合、オフラインであるか、またはネットワークが侵害されている可能性があります。

      fetch(request) .then(response => addToCache(cacheKey, request, response)) .catch(() => fetchFromCache(event))

      ここで、fetchFromCache 関数を使用します:

      function fetchFromCache (event) { return caches.match(event.request).then(response => { if (!response) { // A synchronous error that will kick off the catch handler throw Error('${event.request.url} not found in cache'); } return response; });}

      注意: caches.match でチェックするキャッシュを指定しないで、一度にすべてをチェックします。

      3. オフライン フォールバックを提供する

      ここまでやってもキャッシュに応答できるものがない場合、可能であれば、適切なオフライン フォールバックを返します。 HTMLページの場合、これは/offline/からキャッシュされたページである。 これは、ユーザーにオフラインであることと、ユーザーが求めているものを満たすことができないことを伝える、適度に整えられたページです。

      fetch(request) .then(response => addToCache(cacheKey, request, response)) .catch(() => fetchFromCache(event)) .catch(() => offlineResponse(opts))

      そして、これが offlineResponse 関数です。 キャッシュ ファースト戦略の実装

      HTML コンテンツ以外のリソースのフェッチ ロジックは、キャッシュ ファースト戦略を使用します。 そのため、最初にキャッシュをチェックして、ネットワークの往復を回避します。

      event.respondWith( fetchFromCache(event) .catch(() => fetch(request)) .then(response => addToCache(cacheKey, request, response)) .catch(() => offlineResponse(resourceType, opts)));

      ここでの手順は次のとおりです。

      1. キャッシュから資産を取得しようとする。
      2. それができない場合、ネットワークから (read-through キャッシュで) 取得してみる。

      Offline Image

      offlineResource 関数を実行すると、オフラインのフォールバックとして “Offline” というテキスト付きの SVG 画像を返すことができます:

      function offlineResponse (resourceType, opts) { if (resourceType === 'image') { // … return an offline image } else if (resourceType === 'content') { return caches.match('/offline/'); } return undefined;}

      そして、config:

      var config = { // … offlineImage: '<svg role="img" aria-labelledby="offline-title"' + 'viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">' + '<title>Offline</title>' + '<g fill="none" fill-rule="evenodd"><path fill=>"#D8D8D8" d="M0 0h400v300H0z"/>' + '<text fill="#9B9B9B" font-family="Times New Roman,Times,serif" font-size="72" font-weight="bold">' + '<tspan x="93" y="172">offline</tspan></text></g></svg>', offlinePage: '/offline/'};
      A offline image に関連の更新を行いましょう。 SVG ソースのクレジットは Jeremy Keith にあります。 (大きいバージョンを見る)

      CDN に注意

      フェッチ処理をオリジンに制限している場合、CDN に注意する必要があります。 最初のサービス ワーカーを構築したとき、ホスティング プロバイダーがアセット(画像とスクリプト)を CDN にシャードして、ウェブサイトのオリジン(lyza.com)から提供されなくなったことを忘れていました。 おっと! うまくいかなかった。 結局、影響を受けるアセットの CDN を無効にしました (ただし、もちろんこれらのアセットは最適化します!)。最初のバージョンの完了 サービス ワーカーの最初のバージョンが完了しました。 最適化された応答で該当するフェッチに応答し、キャッシュされたリソースとオフライン時にオフライン ページを提供できる install ハンドラーと詳細な fetch ハンドラーを持っています。

      Chrome では、「デバイス モード」に入り、「オフライン」ネットワーク プリセットを選択することにより、サービス ワーカーがオフラインでどのように動作するかをテストすることができます。 これは非常に貴重なトリックです。 (拡大図を表示)

      フェッチ処理 (serviceWorker.js) を含む完全なコードは GitHub にあります。

      Versioning And Updating the Service Worker

      If nothing were going to change on our website again, we could say we’re done.私たちは、Web サイトを二度と変更しないのなら、これで終わりと言えます。 しかし、サービス ワーカーはときどき更新する必要があります。 キャッシュ可能なパスをもっと追加したくなるかもしれません。 オフラインのフォールバックの方法を進化させたいかもしれない。 Google の Service Worker Precache のように、サービス ワーカーの管理をワークフローの一部にするための自動化されたツールがあることを強調したいと思います。 これを手作業でバージョン管理する必要はありません。 しかし、私のウェブサイトの複雑性は十分低いので、サービス ワーカーの変更を管理するために人間のバージョニング戦略を使用しています。 これは、

      • バージョンを示すシンプルなバージョン文字列、
      • 古いバージョンをクリーンアップする activate ハンドラの実装、
      • 更新したサービス ワーカーの activate を高速化する install ハンドラの更新、から構成されています。

      Versioning Cache Keys

      自分の config オブジェクトに version プロパティを追加できる:

      version: 'aether'

      サービス ワーカーの更新版を配布するときはいつでもこれを変更しなければなりません。 ギリシャ神話の神々の名前を使用するのは、ランダムな文字列や数値よりも興味深いからです。

      注: コードにいくつか変更を加え、接頭辞付きのキャッシュ キーを生成するための便利な関数 (cacheName) を追加しました。 余談ですが、完成したサービス ワーカーのコードで見ることができます。

      Chrome では、「リソース」タブでキャッシュの内容を見ることができます。 私のサービスワーカーのバージョンによって、キャッシュの名前が異なることがわかります。 (これはバージョン achilles です。) (大きいバージョンを見る)

      サービス ワーカーの名前を変更しない

      あるとき、私はサービス ワーカーのファイル名の命名規則をあれこれと変えていました。 これを実行しないでください。 これを行うと、ブラウザは新しいサービス ワーカーを登録しますが、古いサービス ワーカーもインストールされたままになってしまいます。 これは面倒な状態です。

      Don’t Use importScripts for config

      私は、外部ファイルに config オブジェクトを置き、サービス ワーカー ファイルで self.importScripts() を使用してそのスクリプトを取り込むという方法を取りました。 それは、サービス ワーカーの外部で私の config を管理する妥当な方法のように思えましたが、問題がありました。

      ブラウザは、サービス ワーカー ファイルをバイト比較し、それらが更新されたかどうかを判断します (これは、いつダウンロードとインストールのサイクルを再トリガーするかを知る方法です)。 外部の config への変更はサービス ワーカー自体への変更を引き起こさないので、config への変更がサービス ワーカーを更新する原因になっていないことを意味します。 Whoops.

      Adding An Activate Handler

      バージョン固有のキャッシュ名を持つ目的は、以前のバージョンからのキャッシュをクリーンアップできるようにするためです。 アクティベート中に、現在のバージョン文字列が接頭辞として付いていないキャッシュが存在する場合、それらは残骸であるため削除されるべきであることがわかります。 これは現在、待機中のワーカーです。 デフォルトでは、更新されたサービスワーカーは、古いサービスワーカーをまだ使っているページが読み込まれている間は、起動しません。 しかし、install ハンドラを少し変更することで、これを高速化できます。

      self.addEventListener('install', event => { // … as before event.waitUntil( onInstall(event, config) .then( () => self.skipWaiting() ) );});

      skipWaitingactivate をすぐに発生させます。

      では、activate ハンドラを終了します。

      self.addEventListener('activate', event => { function onActivate (event, opts) { // … as above } event.waitUntil( onActivate(event, config) .then( () => self.clients.claim() ) );});

      self.clients.claim は、新しいサービス ワーカーを、そのスコープ内のすべてのオープン ページで直ちに有効にします。

      Chrome で特別な URL chrome://serviceworker-internals を使用して、ブラウザが登録しているサービス ワーカーのすべてを確認することができます。 (拡大図を表示)
      ここで、Chrome のデバイス モードで「オフライン ネットワーク」プリセットを使用して、オフライン時にユーザーが表示するものをエミュレートして、私のウェブサイトを表示しています。 うまくいきました! (拡大図を表示)

      たーだーい!

      バージョン管理されたサービス ワーカーができました。 バージョン管理された更新された serviceWorker.js ファイルは GitHub で見ることができます。

      Further Reading on SmashingMag:

      • A Beginner’s Guide To Progressive Web Apps
      • Building A Simple Cross-Browser Offline To-Do List
      • World Wide Web, Not Wealthy Western Web
      (JB, ML, AL, MSE)

コメントを残す

メールアドレスが公開されることはありません。