[HTML5] アプリケーションキャッシュの使い方

アプリケーションキャッシュは指定したファイルをローカルにキャッシュさせることで、
表示の高速化やオフラインでの動作を可能にするものです。
ブラウザでもその効果は体感できるが、一番威力を発揮するのはスマートフォン環境。
特に速度の遅い3G回線下にあるiPhoneで使うと、まるでWifiを使っているような気分が得られます。

導入方法

あちこちで語られまくってて今更感あるけど一応…。

マニフェストファイルの作成

まずは何をキャッシュするのか指定するマニフェストファイルを作ります。
UTF-8のテキストファイルを開いたら、拡張子を “.appcache” にして適当な名前を付けて保存。
保存する場所はアプリケーションのルートでいいと思います。
保存したらテキストファイルの先頭に

CACHE MANIFEST

と追加。目印なので忘れずに。
その後にキャッシュするものとしないものの設定を書きます。
まずキャッシュするものについての設定は

CACHE:

と書いた下につらつら並べます。コロンも忘れずに。


CACHE:
index.html
js/jquery-1.6.1.min.js
js/main.js
css/html5reset-1.6.1.css
css/style.css
images/logo.png

指定の仕方は相対パスでも絶対パスでもよい。

常にネットワーク上にあるものを参照して欲しいリソースは

NETWORK:

と書いた下に連ねます。

これを指定しなければブラウザが勝手に判断してくれるかというとそうでもなく、
仕様草案で、ブラウザがオンラインの場合でも、利用可能な場合はすべてのリクエストをオフラインキャッシュに対して行うように
と指示されているため、特にNETWORKセクション対応済みのSafariはこれを忠実に守ります。
FirefoxはFALLBACKも含めて未実装で、オフラインの時だけオフラインキャッシュを使うんですが、
Safariはオンライン・オフラインの状況に関わらずオフラインキャッシュを使おうとするので
キャッシュするものはCACHE!しないものはNETWORK!ときっちり書いておかないと
明示しなかった画像が全部ハテナマークになったりする。


NETWORK:
hoge/hoget.cgi
http://www.google-analytics.com/

アプリケーションキャッシュがオフライン動作させるためのものならこの指定はおかしくね?と思ったんだけども
どうもNETWORKで指定してあったとしてもキャッシュするように仕様で定められているらしく
前に書いた仕様草案の通りリクエストが優先されるのはキャッシュみたいです。

アクセスした時何かしら失敗することがあると思います。
失敗時に表示させたいコンテンツは

FALLBACK:

と書いた下に並べておく。
表示変更が必要なければ使わなくても問題はない。

あれ?.manifestって拡張子じゃないの?

「.manifest」だとWindowsのやつと被ることが判明したので変更になりました。

Change the suggested extension for appcache manifests from .manifest to .appcache to avoid clashing with Microsoft’s unregistered application/manifest type

W3Cのサンプルソースでも.appcacheが使われています。

キャッシュの更新を知らせるために

画像やCSSファイルなどのリソースが変更された場合、それをブラウザに通知するにはマニフェストファイルの更新をする必要があります。
普通はファイル名を変えたりしないので、マニフェストファイル内にコメントとしてバージョンや日時を記載して
リソースが変更された時に都度書き換えるという方法を取ります。


CACHE MANIFEST
#2011-06-01

サーバーにMIMEタイプを設定する

作ったファイルをアップロードしても普通はテキストファイルとして扱われてしまうので、
サーバーにcache-manifestだと分からせてやります。

.htaccess

AddType text/cache-manifest .appcache

MIMEタイプと拡張子が結びついていればOKなので、別の拡張子にしても動きます

AddType text/cache-manifest .mf

サーバーの設定が変更出来る権限があるなら直接設定をいじるのもアリです。

manifest属性の追加

htmlの開始タグにmanifest属性でファイル名を指定したら導入完了です。

<html manifest="hoge.appcache">

ブラウザー対応状況

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

※iPhoneとAndroidはOSのバージョンです。

現状、iPhoneのOSは3以下はほぼ絶滅しているので問題はAndroidです。
Android1.6搭載の機種は当然未対応だし、1.6からバージョンアップされた2.0は
初期から2.0が入っている場合と仕様が違うことがあるらしいので注意が必要。

各ブラウザーの挙動

スイッチはhtmlタグに入れるmanifest属性です。
スマートフォンの場合は属性があるときだけキャッシュ機能が有効になり(?)、コンテンツの修正が即時反映されますが
PC版ブラウザはそうもいかず、属性を削除してもキャッシュしたコンテンツが残り続けてしまう。
つまり、保存されたキャッシュファイルの削除は自動では行われないので、手動で消すしかありません。

消し方はブラウザによって異なり、

Safari
→Safariのリセット

Firefox
→ツール>オプション>オフラインデータからドメインを選択して削除ボタンを押す

Chrome
→アドレスバーに about:appcache-internals と入れて選択削除

という感じで非常に頑固。
iPhoneとAndroidの場合は、上記で書いたように属性の有無ですぐ切り替わるので
(ってHTMLファイルがキャッシュされてたらPCブラウザと同じ挙動になって然るべきなのに不思議)
manifest属性がなくなった時点でクリアされているんじゃないかな~と思うんですが
外目には何も変わらないのでよくわからない。

保存や更新をダイアログでユーザーに知らせるか否かは
アプリケーションの仕様やブラウザの設定による所が大きいです。
ユーザーに通知をしたいならJavaScriptを使用する必要があります。

Application cache API

JavaScriptで利用出来るオブジェクトは window.applicationCache です。

Update Status

ステータスコードはwindow.applicationCache.statusで得られます。

  • 0 = UNCACHED … キャッシュされていない
  • 1 = IDLE … manifestファイルが更新されていない
  • 2 = CHECKING … チェック中
  • 3 = DOWNLOADING … ローカルにキャッシュ中
  • 4 = UPDATEREADY… キャッシュの更新が完了
  • 5 = OBSOLETE … manifestファイルのダウンロード失敗、キャッシュを無効化

イベント

  • checking … 更新確認中
  • noupdate … manifestファイルが編集されていない
  • downloading … ローカルへキャッシュ中
  • progress … 更新プロセス進行中
  • updateready … キャッシュの更新完了
  • cached … キャッシュに成功
  • obsolete … マニフェストファイルが404または410を返す場合に発生。キャッシュは削除される
  • error … manifestファイルが404や410を返したか、ダウンロード失敗。またはダウンロード進行中にマニフェストが更新された

メソッド

  • update() … アプリケーションキャッシュのダウンロードプロセスを呼び出す。更新されていない場合はINVALID_STATE_ERR例外をスローする
  • swapCache() … 新しいものがある場合に最新のキャッシュに差し替え、ない場合にINVALID_STATE_ERR例外をスローする

ステータスコードを表示するサンプルソース

ステータスコードとイベントハンドラが比較できます。


var appCache = window.applicationCache;

function cacheStatus(status){
    switch (status) {
      case 0:
        return 'UNCACHED';
        break;
      case 1:
        return 'IDLE';
        break;
      case 2:
        return 'CHECKING';
        break;
      case 3:
        return 'DOWNLOADING';
        break;
      case 4:
        return 'UPDATEREADY';
        break;
      case 5:
        return 'OBSOLETE';
        break;
      default:
        return 'UKNOWN CACHE STATUS';
        break;
    };
}

function handleCacheEvent(e) {
  console.log(cacheStatus(appCache.status), e.type);
}

function handleCacheError(e) {
  console.log('Error: Cache failed to update!');
};

appCache.addEventListener('cached', handleCacheEvent, false);
appCache.addEventListener('checking', handleCacheEvent, false);
appCache.addEventListener('downloading', handleCacheEvent, false);
appCache.addEventListener('error', handleCacheError, false);
appCache.addEventListener('noupdate', handleCacheEvent, false);
appCache.addEventListener('obsolete', handleCacheEvent, false);
appCache.addEventListener('progress', handleCacheEvent, false);
appCache.addEventListener('updateready', handleCacheEvent, false);

ユーザーに更新を通知する

設定によってはブラウザがやってくれるんだけど、100%ではないのでアラートとして表示させるのが無難。

appCache.addEventListener("updateready", function() {
    if (confirm('アプリケーションの新しいバージョンが利用可能です。更新しますか?')) {
        appCache.swapCache();
        location.reload();
    }
}, false);

オンラインの時だけupdateを実行

ブラウザの接続状態がオンラインかどうか、というのはwindow.navigater.onLineで得られるので
if文を使えばオンラインの時だけキャッシュの確認をするように出来ます。

if (navigator.onLine) {
    appCache.update();
}

接続状態が切り替わるとWindow オブジェクトでonline または offline イベントが発生するので
何かしたいときにはイベントリスナーを使う。

window.addEventListener("online", function() {
   console.log('オンラインです');
}, false);

今の所無難に使えそうなのはWebkitで統一できるスマートフォン用のモバイルアプリケーションかな。
オフラインとオフライン両方に気を配らなければならないのは結構手間なんだけど、メリットも多い。
PC用ブラウザ対応は足並みが揃ってないので非常に面倒くさいです。

参考

1件のコメント

Leave a Comment.