PWAではサーバとクライアントサイドはWeb APIでデータのやり取りを行うのが一般的です。サーバサイドレンダリングを多用しているとオフラインでの利用は難しいですし、ユーザ体験としても今ひとつです。
そしてWeb APIを使ってデータの送受信を行う際に厄介なのがキャッシュになります。今回はmBaaSをPWAで利用する際のキャッシュ方法について解説します。
オフライン時の挙動について
PWAは必ずしもオフラインアクセスできなければいけない訳ではありません。オフライン時の扱いは3パターン考えられます。
- エラーにする
- 表示のみ可能とする
- 擬似的に追加、削除も可能とする
ユーザ体験としては3が最も優れたものになるでしょう。しかし、データの追加や削除に関して一時的にキューに入れなければならないこと、さらにネットワークが復帰した際にキューを処理する必要があります。また、オフライン時に変更した内容とサーバ側ですでに修正されたしまったデータとでバッティングが発生する可能性もあります。
2の場合、キャッシュを表示しておいて、追加や削除の処理はネットワークが復帰するまで不可とします。データのメンテナンスはできませんが、参照はできるので大きな問題にはならないでしょう。3に比べて実装量は少ないので、現実的な選択肢といえます。
1はオフライン対応していないのと同じことです。画像やCSSなどのリソースはキャッシュしながらも、Web APIから取得するデータはオフライン対応しないことで、表示自体エラーになります。
mBaaSのデータをキャッシュする際の注意点
2または3の対応について、mBaaSの取得データをキャッシュするでしょう。その際の問題点は、Service Workerのfetch APIは署名処理の伴わない処理だということです。Webブラウザ(メインスレッド)側のデータ取得と、Service Workerのfetchは別なネットワーク処理になります。そのため、次のような処理になります。
1回目のアクセス(Webブラウザからのアクセス)
メインスレッドの処理のため、署名処理が行われます。その結果、適切にデータが取得できます。同時にfetch処理でデータストアの取得が行われますが、こちらは署名がないのでエラーになります。Service Workerでは、このエラーになったコンテンツをキャッシュしてしまいます。
2回目のアクセス
2回目以降のアクセスでは、キャッシュの存在を確認しますので、エラーになったコンテンツをキャッシュしていると、エラーが取得されてしまいます。初回は表示できたのに、2回目以降のアクセスでは常にエラーになってしまいます。これは問題があります。
解決策
この問題あるコンテンツのキャッシュに関する解決策としては、動的コンテンツのキャッシュが挙げられます。これはWebブラウザ側で取得したコンテンツをService Workerに送り、動的にキャッシュを作る方法です。
例えば結果がresultsで取得できていれば、それをService Workerに送信します。
const channel = new MessageChannel(); navigator.serviceWorker.controller .postMessage({ url: DOMAIN, results: results, }, [channel.port2]);
そしてService Worker側では message
イベントが呼ばれますので、それを新しいリクエストとして保存します。resultsというキーを付けて保存するのがポイントです。
self.addEventListener('message', function(event) { // キャッシュを開く caches.open(CACHE_NAME) .then(cache => { // 新しいレスポンスを作る // レスポンスとともに、レスポンスヘッダーも指定 const res = new Response(JSON.stringify({ results: event.data.results} ), { headers: { 'Content-Type': 'application/json' } }); // URLとレスポンスを紐付け cache.put(event.data.url, res); }) });
これでmBaaSのデータを動的にキャッシュできます。
まとめ
PWAでmBaaSを利用する際にはネットワークアクセス部分に気を遣う必要があります。特に取得部分のキャッシュは注意が必要です。登録や更新、削除についてはキューの中に入れておき、ネットワーク接続が回復したら実行するようにしましょう。