HTML5のApplication Cacheを使う

HTML5のApplication Cache機能を使って見ました。いろいろややこしいところもあったのでふりかえり。

# 情報が少ないと思ったら・・・（2015/10/26追記）
どうも情報が少ないと思ったら、非推奨の仕様になっていたようですね。実際にFirefoxでは廃止が決定しています。

 * https://www.fxsitecompat.com/ja/docs/2015/application-cache-api-has-been-deprecated/

記事内に書いてある「Service Workers」というのを使うと良さそうです。

# Application Cacheとはなんぞや？
HTML5のApplication Cache機能とは、あらかじめ定義ファイルで指定したファイルをブラウザのローカル領域に保存し、オフライン時でもサイトの機能を利用することができるようにするための仕組みです。主にタブレットなど、モバイル環境で使用するWebアプリに使うと効果的です。その他スマートフォンで使うWebアプリをあらかじめキャッシュしておくことで、アプリの高速化につなげることもできる・・・とのこと。

## Application Cacheを使った場合、どうなる？
本来Webサイトにアクセスすると、基本的にインターネットサイトのリソースを優先してアクセスします。もしインターネットサイトのリソースが過去のアクセスと全く変化が無かった場合、オフラインキャッシュを使う というくらい。

![image](https://qiita-image-store.s3.amazonaws.com/0/14499/38a98eef-08b9-2b4f-2725-414bfb13a120.png)

それに対して、Application Cacheで指定したファイルは、基本的にキャッシュファイルを最優先で確認します。もしネットワーク上のファイルが更新されていても、定義ファイルが更新されていて、かつJavascriptから更新操作を行うまで、常にキャッシュファイルの方を見ることになりますので注意。

![image](https://qiita-image-store.s3.amazonaws.com/0/14499/3acce14a-bc86-fdab-b8d1-7cfc7172b738.png)

設定を誤ったり、開発中にうっかり有効にしてしまうと、ファイルを更新しても更新したファイルをブラウザが読み込んでくれず、開発が滞ってしまう恐れがありますので注意しましょう。**開発が一区切りつくまで、キャッシュマニフェストは指定しないのがベターです。**

# で、Application Cacheの定義ファイルとは
たぶん自分が説明するより解説サイトを見てもらったほうが早いです。
http://www.html5rocks.com/ja/tutorials/appcache/beginner/

CACHE、NETWORK、FALLBACKの三つのセクションからなるテキストファイルで、HTMLファイルのhtmlタグにmanifest属性をつけて、そこでファイル(キャッシュマニフェスト)を指定します。 


```text:cachemanifest
CACHE MANIFEST
# コメント

CACHE:
キャッシュしたいファイルの名前(URLまたは相対・絶対パス)
キャッシュしたいファイルの名前(URLまたは相対・絶対パス)
キャッシュしたいファイルの名前(URLまたは相対・絶対パス)

NETWORK:
* #CACHEに指定したファイル以外を全てネットワークから読み込む場合の指定
```

キャッシュマニフェストファイルを指定したHTMLファイルにアクセスしたときに、同時にこのCACHEセクションに指定したファイルがすべてダウンロードされます(IE11など一部のブラウザでは、「ダウンロードしてもよろしいですか？」の確認ダイアログが表示される場合があります)。もしサイトで**CDNのファイルにアクセスしている場合は、それらもCACHEセクションに書き込む必要があります**ので、ご注意ください。

以降、CACHEファイルに指定されたファイルにアクセスがあった場合、ネットワークからのダウンロードは行われず、キャッシュファイルから読み込まれます。

## キャッシュを更新するには？
アプリケーションキャッシュが再読込されるのは、以下のタイミングとなります。

 1. キャッシュマニフェストファイルの内容が更新されている状態で、Javascriptの`window.applicationCache.update()`メソッドが呼び出された場合。
 1. キャッシュが存在しない場合

1は結構厄介で、「キャッシュに指定したファイルが更新された」場合でも、キャッシュマニフェストファイルが変更されない限りは、キャッシュの更新は行われません(行えません)。そのため、キャッシュマニフェストファイルの2行目には、キャッシュファイルのバージョン文字列などを仕込んでおき、ファイルは更新されたときにはインクリメントするというのが通例なようです。

```text:cachemanifest
CACHE MANIFEST
# Version: 1
・・・
```

## こんなときどうする？
さて。キャッシュマニフェストの書き方については上のサイトを見れば大抵わかるので、それ以外のポイントを

### データベースなどを参照しており、テーブルの変更時にキャッシュファイルを更新したい場合

こういうニーズがある場合は、キャッシュマニフェストファイルをPHPなどで出力するようにすると良いです。キャッシュマニフェストファイルの条件は、「MIMEタイプが`text/cache-manifest`であること」だけなので


```php:cachemanifest
<?php
  header ( 'Content-type: text/cache-manifest' );
?>
CACHE MANIFEST
# Version: <? echo $date ?>
# dbmod-date: <? echo $moddate ?>
・・・
```

などとしておくと良いです。

#### CakePHPでキャッシュマニフェストファイルを生成する場合
CakePHPを使っている場合、やはりキャッシュマニフェストもCakePHPで出力した方が後々使いやすいです。

ただ、CakePHPだと、header()関数は利用できないようです。CakeResponceオブジェクトのtypeメソッドを利用します(CakePHP 2.6.3で動作確認済み)。

```php:cachemanifest
<?php
App::uses ( 'AppController', 'Controller' );
class AppcacheController extends AppController {

　/* ・・・処理・・・ */

  function beforeFilter() {
    parent::beforeFilter();
    $this->response->type("text/cache-manifest");
  }
}

```

としておくと良いです。beforeFilter()メソッドに書いたのは気分です。他に書いても良いかどうかは未確認。

### PHP/CGI出力をキャッシュしたい場合
サイトをPHPやCGI出力をキャッシュしたい場合は、そのPHPファイルに実際にアクセスする場合に使用するアドレスを指定します。
たとえば`http://onpu-tamago.net/works/define`であれば`http://onpu-tamago.net/works/define`または`/works/define`と書きます。

### 開発中など、強制的にキャッシュファイルを削除したい場合
アプリケーション開発中など、強制的にキャッシュファイルを削除したい場合があります。その場合は、キャッシュファイルを削除することで、もう一度ファイルをダウンロードさせることができます。

Chromeの場合、アドレスバーのfaviconを左クリック→Cookieとサイトデータを表示から、アプリケーションキャッシュを選択し、削除することができます。
![image](https://qiita-image-store.s3.amazonaws.com/0/14499/e9c9f638-0696-07a0-6a19-399022e5a57a.png)


各種モバイルブラウザのキャッシュ削除方法は、検索してみてください。結構見つかります。

## その他の注意点

### iPad(iOS)では、BASIC認証が有効なページでApplication Cacheが使えない
Application Cacheを使っているページにBASIC認証がかかっている場合、
iPadで表示そのページを表示すると、キャッシュの読み込みに失敗します(`window.applicationCache.update()`メソッドを呼び出すと、「InvalidStateError: DOM Exception 11」が表示されます)。

どうもこれは、ちゃんと事前にBASIC認証のパスワードを入力しておいても、URLにユーザー名とパスワードを入力しておいてもダメなようで、キャッシュ対象ファイルの中で一つでも認証が必要であったときに、処理が失敗する模様です。


対策としては、ひとまず拡張子を指定して、関連するファイルのみBASIC認証をスルーするようにするか、そもそもBASIC認証をあきらめるかのどちらかのようです。