Service Worker の登録

Service Worker の登録のタイミングに関するベスト プラクティス。

Service Worker を使用すると、ウェブアプリへの再アクセスを大幅に高速化できますが、Service Worker の初回インストールによって初回訪問エクスペリエンスが損なわれないように対策を講じる必要があります。

一般的に、最初のページが読み込まれるまで Service Worker の登録を延期すると、ユーザー(特にネットワーク接続が低速なモバイル デバイス)のユーザー エクスペリエンスが向上します。

一般的な登録のボイラープレート

Service Worker に関する記事を読んだことがある方は、次のようなボイラープレートを見たことがあるでしょう。

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js');
}

これには、ユーザーにページの更新を通知する手段として、いくつかの console.log() ステートメントまたは以前の Service Worker 登録の更新を検出するコードが伴うことがあります。これらは標準的な数行のコードを 少し変更しただけにすぎません

navigator.serviceWorker.register に違いはありますか?従うべきベストプラクティスはあるか?(この記事がここで終わるわけではないことを考えると)当然のことながら、どちらの答えも「はい」です。

ユーザーの初回訪問

ユーザーが初めてウェブアプリにアクセスしたとします。Service Worker はまだ存在せず、ブラウザは最終的にインストールされる Service Worker があるかどうかを事前に知る方法がありません。

デベロッパーにとっては、インタラクティブなページの表示に必要な最小限のクリティカル リソースをブラウザがすばやく提供できるようにすることが重要です。レスポンスの取得を遅らせるものは、迅速な対話型エクスペリエンスの妨げとなります。

ここで、ページでレンダリングする JavaScript や画像のダウンロード プロセス中に、ブラウザがバックグラウンド スレッドまたはプロセスを開始するとします(簡潔にするため、これをスレッドと仮定します)。たとえば、あなたは大規模なデスクトップ マシンではなく、世界中のほとんどの人が主要なデバイスと見なしているタイプの性能の低いスマートフォンを使用しているとします。この追加のスレッドをスピンアップすると、ブラウザがインタラクティブなウェブページのレンダリングに費やす可能性のある CPU 時間とメモリの競合が発生します。

アイドル状態のバックグラウンド スレッドは、大きな違いを生む可能性は低くなります。しかし、そのスレッドがアイドル状態でなく、ネットワークからリソースのダウンロードを開始する場合はどうなるでしょうか。CPU やメモリの競合について懸念がある場合でも、多くのモバイル デバイスで利用できる帯域幅が限られていることを心配する必要はありません。帯域幅は貴重であるため、同時にセカンダリ リソースをダウンロードして、重要なリソースを損なわないようにしてください。

つまり、新しい Service Worker スレッドを起動してバックグラウンドでリソースをダウンロードしてキャッシュすることは、ユーザーが初めてサイトにアクセスしたときに、インタラクティブになるまでの時間を最短にするという目標に反する可能性があります。

ボイラープレートを改善する

この問題を解決するには、navigator.serviceWorker.register() を呼び出すタイミングを選択して、Service Worker の起動を制御します。シンプルな経験則として、windowload event が配信されるまで登録を遅らせるというものです。次に例を示します。

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js');
    });
}

ただし、Service Worker の登録を開始する適切なタイミングは、ウェブアプリが読み込まれた直後の動作によっても異なります。たとえば、Google I/O 2016 ウェブアプリは、メイン画面に移行する前の短いアニメーションに対応しています。アニメーション中に Service Worker の登録を開始すると、ローエンドのモバイル デバイスでジャンクが発生する可能性があることが発見されました。ユーザー エクスペリエンスを損なうのではなく、アニメーションが終わるまで Service Worker の登録を遅らせました。このとき、ブラウザが数秒間アイドル状態になる可能性が高くなります。

同様に、ページの読み込み後に追加設定を行うフレームワークをウェブアプリで使用している場合は、その作業が完了したことを通知するフレームワーク固有のイベントを探します。

2 回目以降

これまでは初回アクセス エクスペリエンスに焦点を当ててきましたが、Service Worker の登録が遅れるとサイトへの再訪問にどのような影響があるでしょうか。驚く方もいるかもしれませんが、まったく影響はないはずです。

Service Worker が登録されると、installactivateライフサイクル イベントを通過します。Service Worker を有効にすると、ウェブアプリへの以降のアクセスの fetch イベントを処理できるようになります。Service Worker は、スコープ内のページに対するリクエストが行われる前に開始します。既存の Service Worker がページにアクセスする前に実行されていなかった場合、ナビゲーション リクエストの fetch イベントを処理する機会はありません。

したがって、アクティブな Service Worker が発生すると、navigator.serviceWorker.register() をいつ呼び出すかは重要ではありません。実際に呼び出すかどうかは関係ありません。Service Worker スクリプトの URL を変更しない限り、navigator.serviceWorker.register() は以降のアクセスで実質的に no-op です。いつ呼び出されても関係ありません

早期登録をおすすめする理由

できるだけ早い段階で Service Worker を登録するシナリオはありますか?思い浮かぶのは、Service Worker が初回訪問時に clients.claim() を使用してページを制御し、Service Worker が fetch ハンドラ内でランタイム キャッシュを積極的に実行する場合です。この場合、Service Worker をできるだけ早くアクティブにし、ランタイム キャッシュに後で役立つ可能性のあるリソースを入れるという利点があります。ウェブアプリがこのカテゴリにあてはまる場合は、一歩下がって、Service Worker の install ハンドラがメインページのリクエストと帯域幅を奪うリソースをリクエストしないようにすることをおすすめします。

テスト

初回アクセスをシミュレートするには、Chrome のシークレット ウィンドウでウェブアプリを開き、Chrome の DevTools でネットワーク トラフィックを確認します。ウェブ デベロッパーは、ウェブアプリのローカル インスタンスを 1 日に数十回再読み込みすることがあるでしょう。しかし、すでに Service Worker があり、キャッシュにデータが格納されている状態でサイトに再度アクセスしても、新規ユーザーと同様のエクスペリエンスは得られず、潜在的な問題を無視しやすくなります。

次の例は、登録のタイミングによって生じる可能性がある違いを示しています。どちらのスクリーンショットも、ネットワーク スロットリングを使用して低速の接続をシミュレートし、シークレット モードでサンプルアプリにアクセスしたときに撮影されます。

早期登録のネットワーク トラフィック。

上のスクリーンショットは、できるだけ早く Service Worker の登録を行うようにサンプルが変更されたときのネットワーク トラフィックを示しています。ページの表示に必要な他のリソースに対するリクエストと、プリキャッシュ リクエスト(Service Worker の install ハンドラからの、歯車アイコンの横に表示されるエントリ)が分散していることがわかります。

登録が遅れているネットワーク トラフィック。

上のスクリーンショットでは、ページが読み込まれるまで Service Worker の登録が遅延しています。ネットワークからすべてのリソースが取得されるまで事前キャッシュ リクエストは開始されず、帯域幅の競合が排除されていることがわかります。さらに、事前キャッシュするアイテムの一部(サイズ列に (from disk cache) のアイテム)はすでにブラウザの HTTP キャッシュにあるため、ネットワークに再度アクセスすることなく、Service Worker のキャッシュにデータを入力できます。

実際のモバイル ネットワーク上の実際のローエンド デバイスからこの種のテストを実行すると、ボーナス ポイントを獲得できます。Chrome のリモート デバッグ機能を利用すると、Android スマートフォンを USB 経由でデスクトップ マシンに接続し、実行するテストに多くのユーザーの実環境でのエクスペリエンスを実際に反映させることができます。

おわりに

まとめると、ユーザーの初回訪問時に最適なエクスペリエンスを提供することは、最優先すべき課題です。初回アクセスでページが読み込まれるまで、Service Worker の登録を遅らせることで、これを確実に行うことができます。再アクセスしても、Service Worker のすべてのメリットを享受できます。

最初のページが読み込まれるまで Service Worker の初回登録を遅らせる簡単な方法は、以下を使用することです。

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js');
    });
}