サービス ワーカーの登録タイミングに関するベスト プラクティス。
サービス ワーカーを使用すると、ウェブアプリへの再訪問を大幅に高速化できますが、サービス ワーカーの初回インストールでユーザーの初回アクセス エクスペリエンスが低下しないようにする必要があります。
通常、最初のページの読み込みが完了するまでサービス ワーカーの登録を遅らせると、特にネットワーク接続が遅いモバイル デバイスのユーザーにとって最適なエクスペリエンスを提供できます。
一般的な登録ボイラープレート
サービス ワーカーについて読んだことがある場合は、次のようなボイラープレートに遭遇したことがあるでしょう。
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
ページを更新するようユーザーに知らせるために、いくつかの console.log()
ステートメントや、以前の Service Worker 登録の更新を検出するコードが付加されることもあります。ただし、これらは標準のコード数行のマイナーなバリエーションにすぎません。
navigator.serviceWorker.register
にはニュアンスがありますか?推奨事項はありますか?当然ながら(この記事がここで終わるわけではありませんが)、どちらの質問にも答えは「イエス」です。
ユーザーの初回訪問
ユーザーがウェブアプリに初めてアクセスしたとします。まだサービス ワーカーは存在せず、ブラウザは最終的にサービス ワーカーがインストールされるかどうかを事前に知る方法がありません。
デベロッパーは、ブラウザがインタラクティブなページの表示に必要な重要なリソースの最小セットを迅速に取得できるようにすることが優先事項です。これらのレスポンスを取得する速度を遅くするものはすべて、インタラクティブなエクスペリエンスの迅速な提供の妨げになります。
ページのレンダリングに必要な JavaScript や画像のダウンロード中に、ブラウザがバックグラウンド スレッドまたはプロセスを開始するとします(簡潔にするため、スレッドであると仮定します)。高性能なデスクトップ マシンではなく、世界中の多くの人がメインのデバイスと見なしている、低性能のスマートフォンを使用していると仮定します。この追加のスレッドを起動すると、ブラウザがインタラクティブなウェブページのレンダリングに費やす CPU 時間とメモリに対する競合が増加します。
アイドル状態のバックグラウンド スレッドでは、大きな違いは生じないでしょう。しかし、そのスレッドがアイドル状態ではなく、ネットワークからリソースのダウンロードも開始すると判断した場合はどうなりますか?CPU やメモリの競合に関する懸念は、多くのモバイル デバイスで利用可能な帯域幅の制限に関する懸念に比べて、優先度が低くなります。帯域幅は貴重なリソースであるため、セカンダリ リソースを同時にダウンロードして重要なリソースを損なわないでください。
つまり、新しいサービス ワーカー スレッドを起動してバックグラウンドでリソースをダウンロードしてキャッシュに保存すると、ユーザーがサイトに初めてアクセスしたときにインタラクティブなエクスペリエンスをできるだけ早く提供するという目標に反する結果になる可能性があります。
定型文の改善
解決策は、navigator.serviceWorker.register()
を呼び出すタイミングを選択して、サービス ワーカーの開始を制御することです。簡単な目安としては、window
で load
event
がトリガーされるまで登録を遅らせます。次に例を示します。
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js');
});
}
ただし、サービス ワーカーの登録を開始するタイミングは、ウェブアプリの読み込み直後に実行される処理によっても異なります。たとえば、Google I/O 2016 ウェブアプリでは、メイン画面に遷移する前に短いアニメーションが表示されます。アニメーション中にサービス ワーカーの登録を開始すると、低価格帯のモバイル デバイスでジャンクが発生する可能性があることが Google のチームによって確認されました。ユーザー エクスペリエンスを損なうのではなく、サービス ワーカーの登録をアニメーションの終了後まで遅らせ、ブラウザが数秒間アイドル状態になる可能性が高いタイミングに設定しました。
同様に、ウェブアプリでページの読み込み後に追加のセットアップを行うフレームワークを使用している場合は、その処理が完了したことを通知するフレームワーク固有のイベントを探します。
その後の訪問
これまでは初回アクセス時のユーザー エクスペリエンスに焦点を当ててきましたが、サービス ワーカーの登録が遅れると、サイトへの再訪問にどのような影響がありますか?驚かれるかもしれませんが、影響はまったくありません。
Service Worker が登録されると、install
と activate
のライフサイクル イベントが実行されます。Service Worker が有効になると、ウェブアプリへのその後のアクセスに対して fetch
イベントを処理できるようになります。Service Worker は、そのスコープ内のページのリクエストが送信される前に開始されます。ページにアクセスする前に既存のサービス ワーカーが実行されていなかった場合、ナビゲーション リクエストの fetch
イベントを処理する機会はありません。
アクティブなサービス ワーカーが存在する場合、navigator.serviceWorker.register()
を呼び出すタイミングや、呼び出すかどうかは重要ではありません。サービス ワーカー スクリプトの URL を変更しない限り、navigator.serviceWorker.register()
は、その後のアクセスでは事実上無効になります。呼び出されるタイミングは関係ありません。
早期登録する理由
できるだけ早く Service Worker を登録するのが理にかなっているシナリオはありますか?たとえば、サービス ワーカーが clients.claim()
を使用して最初のアクセス時にページを制御し、サービス ワーカーが fetch
ハンドラ内でランタイム キャッシュ保存を積極的に実行する場合です。このような状況では、サービス ワーカーをできるだけ早くアクティブにして、後で役立つ可能性のあるリソースをランタイム キャッシュに格納することが有利です。ウェブアプリがこのカテゴリに該当する場合は、サービス ワーカーの install
ハンドラが、メインページのリクエストと帯域幅を争うリソースをリクエストしていないことを確認してください。
テスト
初回アクセスをシミュレートするには、Chrome のシークレット ウィンドウでウェブアプリを開き、Chrome の DevTools でネットワーク トラフィックを確認します。ウェブ デベロッパーは、1 日に何十回もウェブアプリのローカル インスタンスを再読み込みします。ただし、すでにサービス ワーカーが存在し、キャッシュが完全に入力されている状態でサイトに再度アクセスすると、新規ユーザーが得るエクスペリエンスとは異なるため、潜在的な問題を簡単に見落とす可能性があります。
登録タイミングがもたらす違いを示す例を次に示します。どちらのスクリーンショットも、ネットワーク スロットリングを使用して接続の遅延をシミュレートし、シークレット モードでサンプルアプリにアクセスしたときに撮影されています。

上のスクリーンショットは、できるだけ早くサービス ワーカーの登録を実行するようにサンプルが変更されたときのネットワーク トラフィックを示しています。プリキャッシュ リクエスト(サービス ワーカーの install
ハンドラから送信され、横にギアアイコンがあるエントリ)が、ページの表示に必要な他のリソースのリクエストと交互に表示されます。

上のスクリーンショットでは、Service Worker の登録はページの読み込み後まで遅らせられています。すべてのリソースがネットワークから取得されるまでプリキャッシュ リクエストが開始されず、帯域幅の競合が排除されていることがわかります。さらに、プリキャッシュに保存するアイテムの一部は、ブラウザの HTTP キャッシュ(サイズ列に (from disk cache)
が付いているアイテム)にすでに存在するため、ネットワークにアクセスしなくてもサービス ワーカーのキャッシュにデータを入力できます。
実際のモバイル ネットワーク上の実際のローエンド デバイスからこの種のテストを行うと、さらにポイントが加算されます。Chrome のリモート デバッグ機能を使用して、Android スマートフォンを USB 経由でデスクトップ マシンに接続し、実行するテストが多くのユーザーの実際のエクスペリエンスを反映していることを確認できます。
まとめ
まとめると、ユーザーが初めてサイトにアクセスしたときに最適なエクスペリエンスを提供することが最優先事項です。初回アクセス時にページが読み込まれた後までサービス ワーカーの登録を遅らせると、この点を確実にすることができます。繰り返しアクセスするユーザーに対しては、引き続きサービス ワーカーのメリットをすべて享受できます。
最初のページの読み込みが完了するまでサービス ワーカーの初期登録を遅らせるには、次のようにします。
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js');
});
}