ボクココ

サービス開発を成功させるまでの歩み

Cache API を利用したフロントエンドキャッシュ

ども、@kimihom です。

f:id:cevid_cpp:20190904153717j:plain

今回は Cache API を使う機会があったので、調査結果と利用のユースケースと共に紹介しよう。サーバーサイドをどんなに高速化するよりも、フロントエンドで そもそもリクエストをさせない 仕組みにすれば、それが最も速いというのを改めて感じた。

Service Worker と Cache API

「Service Worker を使えば、オフラインでもアクセスできるようになる」っていうのがよく聞く話だろう。これを実現するには、Cache API と Service Worker の2つを組み合わせることで実現できる。Service Worker を使うと、ページリクエスト時のイベントを横取りすることができるから、それが実現できる。詳しくは以下の Qiita が参考になる。

ServiceWorkerとCache APIを使ってオフラインでも動くWebアプリを作る - Qiita

もちろん Service Worker にはそんな利点もあるけども、普通に PC 向けサービスだったら別にそれが重要になるようなケースってのは少ない。

Web API の一つとして存在する Cache API。今までの私のイメージだと、「Service Worker とペアで利用するもの」だった。しかし、それは下記ページの 1節目 でものの見事に否定されていた。

developer.mozilla.org

このAPIはService Worker スペックで定義されていますが、service workerに結びつけて使う必要はありません。

純粋に JavaScript 上でキャッシュを保存したり取得、削除ができるというわけである。これは、とりわけ SPA のような JavaScrpt 中心のページ構成である場合には特に有益に利用できる。今回 Cache API を利用して実装した点について次章で紹介する。

無限スクロールでのスクロール位置の保存

InfiniteScroll を利用して、データを無限スクロールで表示させるケースはよくあることだろう。Twitter や Facebook などで誰もが当たり前のように無限スクロールを使いこなしている。そして、無限スクロール内でリンクを出すケースはよくある。

そこでいきなり問題が発生する。リンクをクリックして別ページに移り、そして「戻る」ボタンを押すと前のスクロール位置に戻らずにページトップに戻ってしまう。だから続きを読みたいって場合にはまたスクロールして前の位置まで戻る必要がある。

大抵の場合この問題解決の「逃げ」として、リンクを新しいタブで開くって方法がある。なるほど、こうしてブラウザには大量のタブができていくわけだ。タブを閉じてスクロールするページに戻れば、続きから読み進めることができる。この問題を最も簡単に解決できる方法だ。

だけど、このスクロール内のリンクが外部リンクではなく内部リンクで、複数ページが開かれると都合が悪いケースがある。その時は無限スクロール内をクリックされたら、普通にページ遷移して「戻る」でスクロール位置を復帰させる必要が出てくる。

スクロール位置の保存

ということで Cache API を利用して実装してみる。

Cache への保存

キャッシュに保存するのは「無限スクロール内のリンクをクリックした時だけ」となる。そのタイミングで、無限スクロール内に表示されたコンテンツを丸々キャッシュとして保存する。

caches.open(CACHE_VERSON).then(function(cache) {
  var response = new Response(
    $("#contents").html(),
    {headers: new Headers({
      "scroll": $(window).scrollTop()
    })}
  );
  cache.put('/records', response)
});

/records ページ全体として、スクロールした結果をキャッシュに保存するのが大事な点だ。page=3 などのパラメータは含めない。そして、Response を自前で作って、そこに headers に スクロール位置を保存する。

ちなみに、ちゃんと保存されたかどうかは Chrome Developer Console で確認できる。滅多に使わないであろう Applicationタブに表示される。

f:id:cevid_cpp:20190904152834p:plain

キャッシュの読み込み

では「戻る」を押した時にそのキャッシュを読み込めるようにしよう。戻るを押した時に普通のページ遷移では下記は動作しないので気をつけてほしい。これは、History API を使っている前提の話である。もし History API を使わずにって話になると、Service Worker の出番である。

caches.open(CACHE_VERSON).then(function(cache) {
  cache.match('/records').then(function(res) {
    if (res) {
      res.text().then(function(txt) {
        $("#contents").html(txt);
        $(window).scrollTop(parseInt(res.headers.get("scroll")));
      });
    } else {
      callback(); // http request
    }
  });
});

cache.match() でもしキャッシュがあれば、その内容を読み込んで HTML 描画する。そしてヘッダに保存したスクロール位置まで持っていく。もしキャッシュがなければ、通常の Ajax でコンテンツを読み込むようにする。

キャッシュの削除

このままだとページ更新したり、そのページへのリンクをクリックするたびにそのスクロール位置となってしまう。それでは都合が悪いことだろう。てことで「戻る」を押した時以外はキャッシュをクリアしておく必要がある。もちろん、「戻る」以外でもキャッシュ表示させたい場合にはキャッシュを消さないようにすれば OK だ。

caches.open(CACHE_VERSON).then(function(cache) {
  cache.delete('/records');
});

キャッシュがなければ、普通に Ajax で取ってくるようになって、めでたし、めでたし。

終わりに

今回は Web API の中でも Cache API に焦点を当てて、実際の利用ケースを含めて紹介した。

私は ServiceWorker, Local Storage, Session Storage, Cache API など、これらすべての API を駆使してフロントエンドアプリケーションを構築している。一見複雑そうに見えるけど、実際に使ってみるとシンプルで使いやすい API だ。

フレームワークではなく純粋な JavaScript だけを見ても、Web API フロントエンドは奥が深い。ぜひこれらのバックグラウンド処理、ストレージを活用してより快適な Web アプリケーションを構築していこう。

その先にあるのは、私たちの理想郷「快適なウェブアプリケーション」である!