Serviceworker(PWA)とajaxは一緒に使えるのか?


やりたいこと

  • ajaxで取得したデータをサービスワーカー内にキャッシュさせて、オフラインでも表示できるようにしたい。

問題

  • ajaxだとサービスワーカー内にデータを保持できない。
    • キャッシュ対象の中にajaxで取得していたjsonファイルやtxtファイルを書いてみたが、Serviceworker内には保持されていなかった。

解決案

  • 色々試したがajax($getJsonとか)の取得方法だとダメ。
  • デベロッパーツールのネットワークタブ> XHRを見るとXHRかFetchで取得したリソースが並んでいる。どうやら他のリソースをみたところFetch形式で取得したものはServiceworker内にキャッシュされているよう。
  • なんかServiceworkerでfetchという言葉を聞いたことがある。

XHRで取得しているものの確認方法

XHRからfetchで取得する方法に変えたらイケそう。

そもそもXMLHttpRequest、Fetch、$.ajaxって何が違うの?

・非同期通信で、サーバー上のデータを取得する方法としてXMLHttpRequest、Fetch、$.ajaxがある
・fetchはPromiseオブジェクトを返す(thenとかで順番制御がしやすい、短く書ける)
・fetchはXHRからの代替を目指している?
・fetchは新しい技術なのでIEで動かない(Polyfillで実装可能 https://github.com/github/fetch)

実際に修正したコード例

参考: jQuery.getJSON()

参考のソースで例示します。

$.getJsonの場合

example.js
$.getJSON( "ajax/test.json", function( data ) {
  var items = [];
  $.each( data, function( key, val ) {
    items.push( "<li id='" + key + "'>" + val + "</li>" );
  });

  $( "<ul/>", {
    "class": "my-new-list",
    html: items.join( "" )
  }).appendTo( "body" );
});

fetchの場合

動かなかった例

example.js
function getJsonData() {
  var req_bnr = new Request("ajax/test.json", {
    method: "get"
  });
  fetch(req_bnr)
    .then(function (json) {
      var items = [];
      $.each(data, function (key, val) {
        items.push("<li id='" + key + "'>" + val + "</li>");
      });

      $("<ul/>", {
        "class": "my-new-list",
        html: items.join("")
      }).appendTo("body");
    })
}

どうやらjsonがリーダブルな形式(htmlに書き出したり)ではないようです。

参考:Why I still use XHR instead of the Fetch API(英語)

該当箇所を和訳と解釈

  • 最初に呼び出すthenでのresponseではリーダブルな形式でないため return response.json();のプロミスオブジェクトを解決。
    二回目のthenで実際に使いたい形にする(htmlに書き出したり)

  • [応答がネットワークエラーだけエラーとなるが、HTTP 404または500であってもfetch()から返されたPromiseはHTTPエラーステータスを拒否しない。]
    [言い換えれば、呼び出しが失敗したとしても、fetch()はそれを成功のように扱います。]

参考元のコードを使用させて頂き修正。

example.js

// fetchAPI用 返されたBodyをjsonにしてPromise.resolve()する
var handleResponse = function (response) {
  return response.json()
    .then(function (json) {
      if (response.ok) {
        return json;
      } else {
        return Promise.reject(response);
      }
    });
};

function getJsonData() {
  var req_bnr = new Request("ajax/test.json", {
    method: "get"
  });
  fetch(req_bnr)
    .then(handleResponse)
    .then(function (json) {
      var items = [];
      $.each(data, function (key, val) {
        items.push("<li id='" + key + "'>" + val + "</li>");
      });

      $("<ul/>", {
        "class": "my-new-list",
        html: items.join("")
      }).appendTo("body");
    })
}

これを本番ページ向けに修正させたところ、オフラインでもjsonファイルが取得できました!

もともとTypeの列でXHRとなっていましたが、今回の修正でfetch形式で取得できるようになりました。

サービスワーカー内でXHRやajaxは動かないがfetchAPIが動く理由の考察(自論)

参考:Using the Cache API
参考: ServiceWorkerとCache APIを使ってオフラインでも動くWebアプリを作る

参考を読んで何となく解釈すると、

  • fetchAPIだけでなく fetchAPI × cacheAPI(サービスワーカー内で動く)によるもの
  • サービスワーカーjs内のファイル指定(cacheAPIの書き方)
  • リソースをFetch APIでサーバーから取ってきてCache APIを使って保存しておき、Fetchイベントハンドラでキャッシュから取り出して返すということをしているらしい

ありがとうございました。

参考