フロントエンド開発Blog

オレには鈍器がある

WEBアプリケーション , manifest , オフラインファースト

とある事情でオフラインでもそれとなく使えるWEBアプリケーションを作る必要に迫られて色々調べたことを備忘録としてまとめます。

オフラインでどこまで動かすのか

レスポンシブWEBデザインのサイト構築に例えるならブレークポイント決めみたいな感じで、「オフラインでどこまで動作を保証するか」をまず決める必要がありますね。RSSリーダーを例に取るといくつか案はでると思いますが

  • オンラインでしか動作しない(オフライン対応しない)
  • オンラインなら更新し、オフラインは前回の記事一覧を表示(部分対応)
  • オンラインなら更新し、オフラインは前回の記事一覧および全文詳細を表示できる(全力対応)

下に向かって工数が跳ね上がります。どんなユーザが、どんなシチュエーションでオフラインになるのか。十分に考えてどこまで対応するかは初期に決めておくべきかと思います。

オフライン対応時に必要な技術

詳細は一端おいておいて、列挙すると

  1. cache manifest
  2. AJAX
  3. cookieやlocalstorageなどの、データ保存に関する知識

最低限必要な知識はこれだけでした。キャッシュ期間の設定だとか、更新トリガーは何か等々、そういった細かいことは気にせずにささっと作りたいならこれだけで良いということに。HTML5すごい。万歳。

cache manifest

オンラインの資産を「永続的にキャッシュするファイル」と「決してキャッシュしないファイル」に仕分けする役割を担います。HTML的には


<!DOCTYPE html>
<html lang="ja" manifest="cache.manifest">

のように、htmlタグにmanifestというプロパティを追加し、manifestファイルを指定します。この例では、htmlファイルと同じ階層にcache.manifestというファイルがあり、そのファイルをmanifest定義ファイルとして使用します。manifestファイルの書き方は


CACHE MANIFEST
index.html
latestjquery.js
exec.js
style.css

NETWORK:
*

FALLBACK:
err.html
#2014/10/15 17:00:03

こんな感じです。index.html~style.cssはキャッシュしたいファイルに該当します。NETwork:の下には「キャッシュせずに必ずネットワーク通信で取得したいファイル」を指定します。まぁキャッシュしたいファイル以外、ということであれば*でいい気がします。FALLBACKは、キャッシュ周りでなんらかのエラーが生じた場合の代替コンテンツを指定するとのこと。キャッシュ周りは以下の記事がとても参考になりました。

キャッシュマニフェストファイルの用意が済んだらhtaccessに一行追加します。

AddType text/cache-manifest .manifest

これでキャッシュが有効になりました。キャッシュの有無はchromeやfirebugのコンソールで確認できます。

AJAX

オフライン対応、となると「オンラインならA、オフラインならBを表示」のような条件分岐が必要になるケースがほとんどかと思います。静的なHTMLだけではこの条件分岐ができません。かといってバックエンド、たとえばPHPもオフラインでは動作しようがありませんので×。ということは、コンテンツの表示に関わる部分はJSで制御せざるを得ません。

-オンラインかオフラインか検知し、オンラインなら最新コンテンツをAJAXで取得してHTML生成して表示、オフラインならオフライン用に前回保存したデータから復元してHTMLを生成して表示する

ざっくりいえばこれだけですが、JSで制御が必要なことに違いはありませんね。フルAJAXサイトを作るときに近いかなと。

そのため、できあがるHTMLはボタンや決めうちの画像のみの「ガワだけHTML」になります。あくまでコンテンツ部分はJSで制御し、HTMLとして持っておくべき要素はボタンなどのUI類と、コンテンツ出力部の空DIVだけです。

cookieやlocalstorageなどの、データ保存に関する知識

初回訪問時、localstorage等にJSONないしHTMLそのものを文字列として保存しておき、次回以降「オンラインなら最新HTMLを取得し、表示しつつlocalstorageの内容も更新」「オフラインなら更新せず表示のみ」します。

WEBアプリケーション自体のアップデート通知

WEBアプリ側で何らかの変更があって(例えば、画像一式がcache manifestでキャッシュされている状況下で、ロゴを変更した。この場合、キャッシュがクリアされない限り差し替えたロゴは日の目をみない)、コンテンツプロバイダ側で明示的にキャッシュをクリアさせたいときはmanifestの更新と、それを感知するJSを組めば可能。


//manifestに変更がないかチェック
window.applicationCache.addEventListener("checking", function () {
	updateStatus("更新をチェック中...");
	
}, false);
//manifestに変更なし
window.applicationCache.addEventListener("noupdate", function () {
	updateStatus("アプリケーションは最新です");
	
}, false);
//ダウンロード
window.applicationCache.addEventListener("downloading", function () {
	updateStatus("ダウンロードします");
	
}, false);
//キャッシュした
window.applicationCache.addEventListener("cached", function () {
	updateStatus("キャッシュしました");
	
}, false);
//キャッシュに失敗404, 410など
window.applicationCache.addEventListener("error", function () {
	updateStatus("通信に失敗しました");
	
}, false);
//キャッシュ更新準備完了
window.applicationCache.addEventListener("updateready", function () {
	updateStatus("アプリケーション更新チェック");
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
      window.applicationCache.swapCache();
      if (confirm('アプリケーションに更新があります。更新しますか?')) {
            window.location.reload();
      }
    } else {
      // マニフェストに変更はない
    }
}, false);

var updateStatus = function(txt){
	if( window.navigator.onLine ){
		//online
		statusTxt.innerHTML = txt;
		$("body").removeClass("offline");
		
	}else{
		//off
		statusTxt.innerHTML = "オフラインモード";
		$("body").addClass("offline");
	}
}

ざっとこんなかんじのJSを用意しておく。これはmanifestファイルに何らかの変更があったら更新としてユーザに通知し、更新ボタンが押されたらキャッシュを一端破棄して再読み込みが発生する動きになります。

manifestファイルを更新しない限り、再読込されない

ユーザに頻繁にWEBアプリケーションを更新してもらう必要がある場合は、shellなどにmanifestファイルを変更するような仕掛けをしておけばOK。file関数などでmanifestファイルを動的生成し、末尾にunixtimeを#コメントとして追記するなどで対応可能。手動でmanifestを更新するのは手間だし、運用オペレーションミスにも繋がるからCMSベースでコンテンツを管理しているならCRUDに引っかける形でmanifestを更新しておくと漏れが無くていいかもしれない。

誤解を恐れずにいうなら「すんごい強いキャッシュ」ともとれる。これってもしかしてブラウザゲームとか、一度リリースして安定運用期になったらあまり更新しないようなコンテンツには強力なんじゃないかと今更ながら思った次第でござる。。

ページトップへ

関連ページ

ページトップへ