Service Worker との双方向通信

Andrew Guan
Andrew Guan

場合によっては、ウェブアプリは 2 つのサーバー間に双方向の通信チャネルを確立 Service Worker で構成されます

たとえば、ポッドキャスト PWA では、ユーザーが特定のポッドキャストのエピソードを オフラインで消費し、 Service Worker でページへの進捗状況の定期的な情報提供が行われるため、メインの スレッドで UI を更新できます。

このガイドでは、Google Cloud と API の間で双方向通信を実装するさまざまな方法について説明します。 Windowservice ワーカーのコンテキストを Workbox ライブラリなどのさまざまな API、 高度なケースです。

<ph type="x-smartling-placeholder">
</ph> Service Worker とページのメッセージの交換を示す図。

ワークボックスの使用

workbox-window は、一連の Workbox ライブラリのモジュールが ウィンドウコンテキストで実行するだけです。Workbox クラスは、インスタンスの登録済み Service Worker にメッセージを送信するための messageSW() メソッドを提供します。 応答を待ちます。

次のページコードは、新しい Workbox インスタンスを作成し、Service Worker にメッセージを送信します。 次のコマンドでバージョンを取得します。

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

Service Worker は相手側でメッセージ リスナーを実装し、 Service Worker:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

ライブラリは内部でブラウザ API を使用しています。これについては、次のセクションで確認します。メッセージ チャネル。ただし、 より使いやすい実装の詳細を提供し、ワイドブラウザを活用することも可能 サポートしています。

ワークボックス ウィンドウを使用した、ページと Service Worker 間の双方向通信を示す図。

ブラウザ API を使用する

Workbox ライブラリではニーズを満たせない場合は、いくつかの下位レベル API を使用して、 ページと Service Worker 間の「双方向」通信を実装するいくつかの類似点 相違点を見ていきましょう。

類似点:

  • いずれの場合も、通信は一方の端で postMessage() インターフェースを介して開始され、 message ハンドラを実装します。
  • 実際には、利用可能なすべての API で同じユースケースを実装できますが、 開発が簡素化されることもあります

相違点:

  • コミュニケーションの相手側を特定する方法はさまざまです。 他のコンテキストへの明示的な参照と、他のコンテキストはプロキシ経由で暗黙的に通信できる 各側面でインスタンス化されます
  • ブラウザによって対応状況は異なります。
ページと Service Worker 間の双方向通信、および使用可能なブラウザ API を示す図。

Broadcast Channel API

対応ブラウザ

  • Chrome: 54。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: 38。 <ph type="x-smartling-placeholder">
  • Safari: 15.4。 <ph type="x-smartling-placeholder">

ソース

Broadcast Channel API BroadcastChannel を介してブラウジング コンテキスト間の基本的な通信を可能に オブジェクト

これを実装するには、まず、各コンテキストが同じ ID で BroadcastChannel オブジェクトをインスタンス化する必要があります。 メッセージを送受信します。

const broadcast = new BroadcastChannel('channel-123');

BroadcastChannel オブジェクトは postMessage() インターフェースを公開し、リッスンしている視聴者にメッセージを送信します。 context:

//send message
broadcast.postMessage({ type: 'MSG_ID', });

任意のブラウザ コンテキストで、BroadcastChannelonmessage メソッドを介してメッセージをリッスンできます。 オブジェクト:

//listen to messages
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process message...
  }
};

ご覧のとおり、特定のコンテキストへの明示的な参照はないため、 Service Worker や特定のクライアントを最初に参照します。

ブロードキャスト チャネル オブジェクトを使用した、ページと Service Worker 間の双方向通信を示す図。

デメリットは、このドキュメントの作成時点で、この API が Chrome、Firefox、 Safari などの他のブラウザではサポートされていません まだです

Client API

対応ブラウザ

  • Chrome: 40。 <ph type="x-smartling-placeholder">
  • Edge: 17。 <ph type="x-smartling-placeholder">
  • Firefox: 44。 <ph type="x-smartling-placeholder">
  • Safari: 11.1。 <ph type="x-smartling-placeholder">

ソース

Client API を使用すると、 Service Worker が制御しているアクティブなタブを表すすべての WindowClient オブジェクトへの参照。

このページは単一の Service Worker によって制御されているため、 アクティブな Service Worker を serviceWorker インターフェースから直接取得する方法:

//send message
navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
});

//listen to messages
navigator.serviceWorker.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process response
  }
};

同様に、Service Worker は onmessage リスナーを実装してメッセージをリッスンします。

//listen to messages
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //Process message
  }
});

Service Worker は、クライアントと通信するために、 WindowClient オブジェクトを取得するには、次のコマンドを実行します。 メソッド Clients.matchAll()Clients.get()。その後、 次のいずれか postMessage():

//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    //Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});
Service Worker がクライアントの配列と通信する様子を示す図。

Client API は、Service Worker のアクティブなすべてのタブと簡単に通信する場合に適しています。 比較的単純明快な方法で行えます。この API は、 ブラウザ、 ただし、一部のメソッドは利用できない場合があります。事前にブラウザのサポート状況を サイトに実装することをおすすめします

メッセージ チャンネル

対応ブラウザ

  • Chrome: 2. <ph type="x-smartling-placeholder">
  • Edge: 12。 <ph type="x-smartling-placeholder">
  • Firefox: 41。 <ph type="x-smartling-placeholder">
  • Safari: 5. <ph type="x-smartling-placeholder">

ソース

メッセージ チャネルの要件 ポートを定義してあるコンテキストから別のコンテキストに渡し、双方向通信を確立する

チャンネルを初期化するために、ページで MessageChannel オブジェクトをインスタンス化し、それを使用します。 登録済みの Service Worker にポートを送信します。また、このページには onmessage リスナーが実装されています。 他のコンテキストからメッセージを受信します。

const messageChannel = new MessageChannel();

//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

//Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};
双方向通信を確立するために、ポートを Service Worker に渡すページを示す図。

Service Worker はポートを受信し、そのポートへの参照を保存し、そのポートを使用してもう一方のポートにメッセージを送信します。 side:

let communicationPort;

//Save reference to port
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

//Send messages
communicationPort.postMessage({type: 'MSG_ID'});

MessageChannel は現在、主要な ブラウザ

高度な API: バックグラウンド同期とバックグラウンド フェッチ

このガイドでは、比較的シンプルな双方向通信手法の実装方法について 実行する操作を説明する文字列メッセージや URL のリストを あるコンテキストから別のコンテキストにキャッシュ保存するというものです。このセクションでは、特定のデータを処理する 2 つの API 接続できない、ダウンロードに時間がかかるといったシナリオです。

バックグラウンド同期

対応ブラウザ

  • Chrome: 49。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: サポートされていません。 <ph type="x-smartling-placeholder">
  • Safari: サポートされていません。 <ph type="x-smartling-placeholder">

ソース

チャットアプリで、接続不良によってメッセージが失われないようにする必要がある場合があります。「 Background Sync API: ユーザーの接続が安定したときに再試行されるようにアクションを延期する。こうすると、 ユーザーが何を送信したいとしても 実際には送信されます

このページでは、postMessage() インターフェースの代わりに sync を登録します。

navigator.serviceWorker.ready.then(function (swRegistration) {
  return swRegistration.sync.register('myFirstSync');
});

Service Worker が sync イベントをリッスンしてメッセージを処理します。

self.addEventListener('sync', function (event) {
  if (event.tag == 'myFirstSync') {
    event.waitUntil(doSomeStuff());
  }
});

関数 doSomeStuff() は、エラーの内容の成功/失敗を示す Promise を返す必要があります。 できます。処理が完了すると、同期が完了します。失敗した場合、別の同期のスケジュールが 再試行してください。再試行の同期も接続を待機し、指数バックオフを使用します。

オペレーションの実行が完了すると、Service Worker はページと通信し、 前述の通信 API のいずれかを使用して UI を更新する

Google 検索はバックグラウンド同期を使用して、接続が不安定なために失敗したクエリを保持し、再試行します ユーザーがオンラインになったときに通知を受け取れます。オペレーションの実行後、チームはその結果を 通知することもできます。

<ph type="x-smartling-placeholder">
</ph> 双方向通信を確立するために、ポートを Service Worker に渡すページを示す図。

バックグラウンド フェッチ

対応ブラウザ

  • Chrome: 74。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: サポートされていません。 <ph type="x-smartling-placeholder">
  • Safari: サポートされていません。 <ph type="x-smartling-placeholder">

ソース

メッセージの送信やキャッシュする URL のリストなど比較的短い処理については、 おすすめします。タスクに時間がかかりすぎると、ブラウザがサービスを強制終了する ユーザーのプライバシーやバッテリーが危険にさらされます。

Background Fetch API を使用すると、映画、ポッドキャスト、レベルのダウンロードといった長いタスクを Service Worker に任せることができます。 できます。

ページから Service Worker と通信するには、backgroundFetch.fetch postMessage():

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch(
    'my-fetch',
    ['/ep-5.mp3', 'ep-5-artwork.jpg'],
    {
      title: 'Episode 5: Interesting things.',
      icons: [
        {
          sizes: '300x300',
          src: '/ep-5-icon.png',
          type: 'image/png',
        },
      ],
      downloadTotal: 60 * 1024 * 1024,
    },
  );
});

BackgroundFetchRegistration オブジェクトを使用すると、ページで progress イベントをリッスンし、 ダウンロードの進行状況を確認します。

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(
    (bgFetch.downloaded / bgFetch.downloadTotal) * 100,
  );
  console.log(`Download progress: ${percent}%`);
});
<ph type="x-smartling-placeholder">
</ph> 双方向通信を確立するために、ポートを Service Worker に渡すページを示す図。
ダウンロードの進行状況を示すように UI が更新される(左)。Service Worker のおかげで、すべてのタブを閉じてもオペレーションの実行を継続できます(右)。
で確認できます。

次のステップ

このガイドでは、ページワーカーと Service Worker の間のコミュニケーションの最も一般的なケースについて説明しました。 (双方向通信)。

多くの場合、一方のコンテキストがもう一方のコンテキストと通信するのに 1 つのコンテキストのみを必要とする場合があり、 レスポンスが返されます。単一方向の手法を Google Cloud 環境で実装する方法については、次のガイドをご覧ください。 Service Worker とページの間のやり取り、ユースケースと本番環境での例を以下に示します。

  • 命令型キャッシュに関するガイド: ページから Service Worker を呼び出して 事前にリソースをキャッシュに保存します(プリフェッチのシナリオなど)。
  • 更新のブロードキャスト: Service Worker からページを呼び出して、 重要な更新について通知を受け取れます(たとえば、ウェブアプリの新しいバージョンが利用可能になったときなど)。