実験的に自分のポートフォリオサイトにService Worker Cache APIを導入しようとして結局断念しました。 この記事は、この失敗の経験の分析と共有のためのものです。
この記事は以下の読者を想定しています。
ほとんどService Workerの知識のない初学者を想定しています。 私自身も今回はじめてServiceWorkerに触れました。同じような経験と知識の方を想定しています。
この記事では、以下のような環境を前提として書かれています。 環境の変化によって内容に齟齬が生じますのでご注意ください。
今回は自分のポートフォリオサイトを実験環境に利用しました。 動的な情報取得や処理がない静的ページです。
sw-precacheはGoogle製のNodeモジュールです。開発環境で出力されたファイルをチェックし、CacheAPIに特化したServiceWoker.jsファイルを自動的に生成します。ただし更新頻度が低下しているようで、今から利用する場合は後継にあたるWorkboxを利用することをオススメします。
gulpはsw-precacheの処理と静的ファイルのrevision管理を統合するために利用しています。
ServiceWorkerの動作にはhttpsが必須です。私の記事などを参考にして自己証明書を作成し、開発環境を整えてください。
これらのAWSサービスを組み合わせた配信環境を利用しています。
Service WorkerのCacheAPIの目的と役割を理解しないまま漠然と、以下のような機能であると想像していました。
しかし実際には、Service WorkerのCacheAPIには想像していたものとは別の目的があり、以下のような挙動をしました。
これらの挙動はCacheAPIの目的には合致しており、目的にそった実装を行うことで真価を発揮します。
Service Worker Cache APIの目的はService Workerのライフサイクルから読み取れます。 このAPIは以下の目的にフォーカスしています。
これらの目的の達成のために、Service Worker Cache APIは前段の挙動をします。
それぞれの挙動を例に、どのような目的を持ったものなのかを考えていきます。
Service Worker Cache APIには複数のキャッシュ戦略があります。オフラインでもページを問題なく表示させるためには、事前にhtmlとcssなどの依存ファイルをまとめて読み込んでおくプレキャッシュの方式を取ることになります。
この方式は、砕けた理解をする場合「Chromeの自動更新っぽいやつ」と考えられます。Chromeを例にすると
となります。まずは起動と動作をすることを優先し、更新確認はバックグラウンドで実行されます。誤って複数のバージョンのアプリケーションが並走してしまい、データを破壊することがないように、更新の実行はシャットダウンを確認してから行われます。
sw-precacheで生成されたServiceWorkerとプレキャッシュ対象ファイルもこれによく似た挙動をします。
したがってプレキャッシュの場合、初回インストール後キャッシュ対象に指定しているhtmlを更新すると、それが適用されるのは3回目の訪問時です。2回目の訪問時は初回訪問時のキャッシュでページをとにかくページを表示し、バックグラウンドでキャッシュの更新をしています。
この挙動はオフラインファーストという目標に合致したものです。
ServiceWorkerは独立したスレッドで実行されます。メインスレッドとは独立しており、必要に応じて起動、待機、応答します。 インストール済みのServiceWorkerは、対象となるサイトの読み込みが始まると事前に起動します。この起動の処理には時間がかかります。手元の環境では100ms前後この処理にかかっているようでした。
sw-precacheのstaticFileGlobsオプションで指定されたファイルは、モジュールの名の通り、プレキャッシュを行います。これは表示されるか否かに関係なく、とにかくサーバーから指定されたリソースを事前にバックグラウンドで読み込むという動作です。この挙動はオフライン動作に必須のファイルを読み込むためには優れた方法です。
しかし、無秩序にファイル指定をしてしまうと転送量は増え続けます。実際には表示されないコンテンツもプレキャッシュ対象に指定されれば読み込まれていますので、その分の転送量は増加していることになります。 ユーザーの操作シナリオを想定して、クリックなどの操作を行わないと読み込まれないはずの大きな画像などはプレキャッシュの対象から除外することを検討する必要があります。ユーザー操作時やネットワーク応答時にキャッシュできるかもしれませんし、そもそもキャッシュする必要がないかもしれません。
Cache APIの目的と挙動が理解できてきたところで、現状の問題と、事前に設計を必要とする点が見えてきました。
結果的に今回の実験的な導入は、転送量が大きく、起動に時間がかかり、htmlの更新確認が遅くなるという散々な結果に終わりました。しかし、この結果からService Worker Cache APIを積極的に導入すべきサイトの形が見えてきました。
SPAとService Workerの相性は非常に良いです。もともとコンテンツとhtmlが分離されており、キャッシュ戦略が立てやすい関係にあります。また、オフラインで動作することにも大きな価値があります。
コンテンツ中心ではなく、機能中心のWebアプリはService Workerの恩恵を大きく受けられます。ローカルのjavascriptとローカルストレージ内のデータで大半の処理が完結できる場合、Webアプリというよりはたまに更新確認をするローカルのアプリケーションという感じになります。
再訪問率が高く、またサイト内巡回時間が長いサイトほどキャッシュの恩恵を受けられます。 Pinterest PWAのパフォーマンス改善事例 Pinterestの例の場合、各記事はユニークURLを発行していますが、ページ遷移は発生せずSPAであることがわかります。また、サービスの性質上ネットワークから最新のコンテンツを取得し続けるアプリなので、オフライン対応はしていません。Service Workerを永続し制御が可能なブラウザキャッシュとして利用しています。
今後の課題としてWorkboxをまだ利用していないので、今後はこのモジュールでキャッシュ戦略とコンテンツの分類が可能かどうかに挑戦します。Gulpとの協調動作も可能なので、gulp-revをそのままにsw-precacheの置き換えを狙います。
これに懲りずどうにか問題を解決し、別のアプローチ、もしくは別のターゲットでの導入を目指します。
以上、ありがとうございました。