HTML5 × CSS3 × jQueryを真面目に勉強してみる – #3 オフラインWebアプリケーション

2012.02.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

そんな訳で、HTML5のオフラインWebアプリケーションに触れてみることにします。


前置きのようなもの

Webアプリケーションというくらいだから、当然ネットワークに接続し、目的のページをダウンロードして表示するわけです。 ということはオフライン状態では、Webアプリケーションは一切使うことができないということになります。そりゃそうだ。

ではオンライン状態の間にWebアプリケーションのリソースを、ローカルのストレージ領域にごっそりキャッシュ(ダウンロード)してしまったらどうでしょう? 以降はそのリソースを参照すれば、オフラインだろうが問題なっしんぐだし、オンラインであってもHTTP通信するよりパフォーマンスの向上が見込めますよね。 この単純明快な力技こそが、HTML5のオフラインWebアプリケーションの仕組みなのです。

とはいっても、アクセスしたページがHTML5なら片っ端からキャッシュするという訳ではありません。 キャッシュしておきたいリソースは、あらかじめマニフェストファイルと呼ばれるモノに記述しておく必要があります。 このマニフェストファイルをHTMLファイルが参照し、Webブラウザがそのページにアクセスすることで記述されたリソースがキャッシュされるという訳です。 オフラインWebアプリケーションは、このキャッシュマニフェストファイルをメインに動作していきます。

各ブラウザ毎の対応状況

IE Firefox Safari Chrome Opera Android iPhone
× 3.5〜 4.0〜 5.0〜 10.6〜 2.0〜 2.1〜

うん。あれだ。
SmallなことはNo thank youということで。

キャッシュマニフェスト

前置きでも書きましたが、オフラインWebアプリケーションはマニフェストファイルがキモとなってきます。 ざっくり言うと、マニフェストファイルはWebアプリがオフライン状態の間にアクセスするかもしれないリソースを一覧にまとめたモノとなります。

もう少し突っ込んでみると…、

  • 基本的にはただのテキストファイル
  • 拡張子は.appcache
  • 文字コードはUTF-8
  • キャッシュしておきたいリソースのURLを、一行ずつ記述する
  • 逆にキャッシュしたくないリソースのURLも、同様に記述して定義しておくことができる
  • キャッシュされなかったページにアクセスした際の代替リソースを定義しておくことができる

だいたいこんな感じでしょうか。具体的な書き方等については、この後に少しずつ紹介してきます。

マニフェストファイルの書き方

CACHE MANIFEST
#version 1.0 02-13-2012

CACHE:
img/1.jpg
img/2.jpg

FALLBACK:
/ offline.html

NETWORK:
*

1行目は、CACHE MANIFESTで始まります。これより前に改行や空白を入れることは出来ませんし、大文字で書く決まりになっています。

それ以降は、大きく3つのセクションに分けることができます。

セクションヘッダー セクションの説明
CACHE: 明示リスト
キャッシュするリソースを定義するセクション
FALLBACK: 代替リスト
キャッシュファイルが見つからなかった、キャッシュされなかったページにアクセスした際の代替リソースを定義するセクション
NETWORK: オンライン指定リスト
キャッシュせずにオンラインから取得するリソースを定義するセクション

ちなみにセクションヘッダーが記述されていない場合は、明示リスト扱いとなります。

ブラウザはキャッシュマニフェストファイルをロードした後、Webサーバーのルートディレクトリから明示リストにあるリソースを全てダウンロードします。ダウンロードが無事完了してしまえば、たとえLANケーブルを引っこ抜いてページを更新しても、これらすべてのリソースはローカルのストレージ領域から読み込まれ、何事も無かったかのように利用できるという訳です。すげー。

ちょっとしたポイント 1

ひとつ注意しておかなくてはならないのが、明示リスト内にひとつでも見つからない(404エラー)リソースがあるとエラーとなり、全くキャッシュしてくれないというのがあります。

ちょっとしたポイント 2

通常、HTMLファイルをキャッシュマニフェストのリストに記述する必要はありません。仮にWebアプリケーションが単一のページで出来ているのであれば、そのページがマニフェストファイルを参照しているだけで、ブラウザはそのページもWebアプリケーションの一部と判断するので、一緒にキャッシュしてくれます。 ただし、Webアプリケーションが複数のページで構成されている場合は、それらのHTMLファイルを明示リストに記述しておかなくてはなりません。

ちょっとしたポイント 3

キャッシュマニフェストのリストに記述されたリソースは、キャッシュマニフェストの削除操作をしない限り破棄されずにブラウザに残り続けます。また、キャッシュマニフェストが更新されない限り、キャッシュ済みリソースはサーバー上で更新されたとしても、ブラウザはそんなこと知らないので、過去にキャッシュされた古いリソースを参照し続けます。 そのため、明示リストに記述されたリソースを更する場合は、マニフェストファイルも同時に更新する必要があります。更新対象はコメント行も含まれるので、バージョンの値を書き換えるといった管理にすればOK。

NETWORKセクション

CACHEセクションとは逆に、キャッシュせずに毎回必ずオンラインで取得したいリソースをここに記述します。上の例文では*(アスタリスク)と書いていますが、これはCACHEセクションに記述されているリソース以外全てを対象にするという意味です。

FALLBACKセクション

オンラインリソースのキャッシュに失敗ないし出来なかった際に、表示したい代替リソースをここに記述する訳ですが、上のサンプルではちょいとばかし変な書き方をしています

FALLBACK:
/ offline.html

行の先頭に「/」とあり、半角スペースあけてoffline.htmlと代替リソースを記述しています。どういう事なの…?

まず次のような前提があります。アクセスしたページがマニフェストファイルを参照していたら、そのHTMLページはFALLBACKセクションに記述されていない限り無条件でキャッシュされます。これらはAppCacheと呼ばれる領域へ追加されます。そしてオフライン時は、このAppCacheから目的のリソースを探しだして、見つかればそのページを表示します。

「/」とだけ書いた場合、Webサーバーのルートディレクトリ以下全てに対してAppCacheに追加されていないかチェックしに行きます。もし存在すればキャッシュリソースを表示し、無ければ代替リソースとしてoffline.htmlを表示という訳です。
ちなみに、

FALLBACK:
/index.html offline.html

このように記述した場合は、index.htmlが見つからなかったときのみoffline.htmlを表示します。

マニフェストファイルをWebサーバーに置く

マニフェストファイルの基本的な書き方は分かりました。実際に作成してWebサーバーにおいてみたいところですが、フツーにHTMLファイルやCSSファイルと同様に、任意のディレクトリに置くだけでOK!なーんて簡単には行かないのがキャッシュマニフェストの憎いところです。

MIMEタイプを設定する

そのままWEBサーバーに置いただけでは、サーバーはマニフェストファイルを単なるテキストファイルとしてしか扱ってくれません。なのでMIMEタイプを設定してサーバーにバシッと分からせる必要があるわけです。以下その手順です。
※ApacheベースのWebサーバーの場合を例にとってみます。

  1. Webディレクトリのルートに.htaccessファイルを作成する ※既に作成済みならばソレを使います。
  2. 以下の1行を記述する

 AddType text/cache-manifest .appcache

マニフェストファイルの拡張子を.appcacheと定義しています。ぶっちゃけMIMEタイプと拡張子がキチンと関連付いていればOKなので、.yamadaでも動きます。

.appcacheファイルの代わりにhttpd.confに同様の記述をしてもOKですが、レンタルサーバーなどでは編集できないことが殆どなので、.htaccessを使うのが無難でしょう。

マニフェストファイルを指定する

さいごにmanifest属性を追加します。htmlタグにファイル名を指定すればOKです。

<!DOCTYPE html>
<html lang="ja" manifest="cache.appcache">
  <body>
  ・・・
  </body>
</html>

Application cache API

キャッシュマニフェストを参照しているページをブラウザが訪問すると、DOMイベントが発生します。 イベントは全てwindow.applicationCacheオブジェクトで発生します。

ステータスコード

window.applicationCache.statusで取得することができます。

0 = UNCACHED キャッシュされてないよ
1 = IDLE マニフェストファイルが更新されてないままだよ
2 = CHECKING 更新をチェックしてるよ
3 = DOWNLOADING ローカル領域にキャッシュしてるよ
4 = UPDATEREADY 最新のキャッシュが出来たよ
5 = OBSOLETE マニフェストファイルが見つからないよ

イベント

checking 更新がないかチェックしているよ
noupdate マニフェストファイルが更新されてないままだよ
downloading ローカルへキャッシュ中だよ
progress 更新プロセスが進行しているよ
updateready 最新版が全部キャッシュできたよ
cached キャッシュは完全に終わったよ いつでもオフラインで使えるぜ
obsolete マニフェストファイルが見つからないよ
error ・キャッシュマニフェストがHTTP 404 もしくは410エラーを返したよ
・マニフェストファイルは更新されてないけど、参照しているHTMLページが正常にダウンロードできなかったよ
・マニフェストファイルがアップデートの途中で変更されたかな?
・キャッシュマニフェストのリストにあるリソースのダウンロードができなかったよ

メソッド

update() キャッシュのダウンロード処理を実行するよ 何も更新されていなかったら、例外としてINVALID_STATE_ERRをスローするよ
swapCache() キャッシュを最新版に差し替えるよ もし見つからなかったら、例外としてINVALID_STATE_ERRをスローするよ

ブラウザがオンラインかどうかチェックしたい

JavaScriptにnavigator.onLineというAPIがあるので、コレを利用すればOKです。

if (navigator.onLine) {
  console('オンラインです。');
} else {
  console('接続されていませんね。');
}

ブラウザの接続状態が切り替わると、windowオブジェクトにonlineofflineイベントが発生します。

window.addEventListener('online', function(event) {
  console('オンラインになりました。');
  //データの再取得処理とかを記述…
}, false);

window.addEventListener('offline', function(event) {
  console('回線が切断されました。');
  //実行途中の処理をキューに格納しておくとか… 
}, false);

キャッシュデータの確認と削除

ローカルにキャッシュされたデータの確認や削除方法は、ブラウザによって異なります。

Chrome

アドレス欄にchrome://appcache-internals/と入力します。

Firefoxの場合

アドレス欄にabout:cacheと入力します。


キャッシュされたリソースを削除する場合は、オプションメニューの詳細ネットワークオフライン作業用のデータを保存しているWebサイト:から対象のデータを選択して削除ボタンをクリックします。

Safariの場合

メニューから編集キャッシュを空にする…をクリック

Operaの場合

メニューから設定詳細設定ストレージ永続ストレージを使用するウェブページから対象のデータを選択して削除ボタンをクリックします。

PC版ブラウザに関しては、これらの方法で確認や削除が可能ですが、モバイル端末は簡単には行きません。iOSにおいては、iTunesに接続して復元処理をしないと削除が出来ないし、Androidに至っては、機種によって一切関与出来ない場合があります。Xperia arcに関しては、ブラウザの設定メニューからローカルストレージの削除項目があるのを確認しましたが、IS03とGalaxy Nexusは、それらしい項目を見つけられませんでした。やれやれ…

デバッグ時の注意点

Webサーバーはブラウザに対して、静的なファイルは数時間キャッシュするようにと指示をします。これはHTTPキャッシュの正常な仕様であってバグではありません。ということは、キャッシュマニフェストを作成してサーバーに置き、その後すぐにリソースを追記しても数時間の間はブラウザはキャッシュマニフェストを再取得してくれないことになります。

そのため、開発環境においては、.htaccessファイルに以下の2行を記述しておきます。

ExpiresActive On
ExpiresDefault "access"

こうしておくことで、.htaccessファイルのあるディレクトリとすべてのサブディレクトリにあるファイルがHTTPキャッシュされなくなります。

まとめ

モバイルにおいてもオフラインWebアプリケーションというのは、まだそれほど実例が無いのかもしれません。Gmailは非常にレベルの高いオフライン機能が実装されており、自分も地下鉄に乗っているときなどは良くその恩恵に授かっていたりします。

実際に開発するにあたっては、オンラインとオフラインとが切り替わった時の挙動に対する対応がヘビーなところかもしれません。

このキャッシュマニフェストに加えて、前回のWebStorageや今後紹介予定のIndexedDBとうまく組み合わせることで、ネイティブにも負けない強力なモバイル向けWebアプリケーションが実現できるのではないでしょうか。

参考にどうぞ