Skip to content
uupaa edited this page Oct 8, 2013 · 4 revisions

Wingについて

Wing は、サーバ上のアセットファイルをブラウザ上にキャッシュし、アセットの変更に自動追従する機能を持ったクライアントサイドキャッシュライブラリです。
Wing は Tombo の一部ですが、単体でも利用可能です。

以下の特徴があります。

  • 通信の回数と量を減らし、ユーザの待ち時間を最小化します
    • ファイル毎に更新確認のための通信を行いません。更新チェックはマニフェストファイルで一括して行います
    • ファイル毎の更新確認が不要になり、その時間を使ってcssやjsなどのページの表示に必要なファイルを処理できます。結果的にページが素早く表示されるようになります
  • アセットをマニフェストで管理します(台帳で管理します)
    • マニフェストは、アセットとキャッシュに関する情報(ディレクトリ、ID, パス, エンコード方法等)をまとめたJSONデータです
    • Wing は初期化時にマニフェストをサーバから取得し、自動的に追従します
  • キャッシュ可能なデータ量は 3.6〜5.0MB です
    • バイナリデータが多いほどキャッシュ容量は目減りします
    • バイナリデータはテキストデータの1.33倍の容量が必要になります
    • ファイル単体で2MBを超す大きなファイルはキャッシュできません(例: WebFontファイル)
    • iOS では一部のデータ(mp3やaac)はキャッシュできません
    • Wing で保存できないデータや、アセットが巨大な場合は、ApplicationCache の利用を検討してください

Wing と 従来のキャッシュ方法の比較

Wing によるクライアントサイドキャッシュと、従来の方法を比較です。

手段/方法 キャッシュの
永続性
キャッシュの
部分更新
通信の抑止 導入のしやすさ キャッシュ容量
ブラウザキャッシュ × × 不明 (端末や実装に依存)
ApplicationCache × 2.5MB (公称 5MB)
ETag ヘッダ × × 不明
Expires ヘッダ × 不明
CDN × ---
Wing 3.6 〜 5MB
Zabuton 800MB以上可能 (上限は端末に依存)
  • ブラウザキャッシュ
    • MobileSafari のブラウザキャッシュはデバイスの再起動で消えてしまいますが、Wingのキャッシュは消えません
    • ブラウザキャッシュは、キャッシュが溢れると順番に消えてしまいますが、Wingのキャッシュは消えません
  • ApplicationCache
    • ApplicationCache は一部だけ更新できず、一旦全てのファイルを更新しようと通信が発生しますが(その結果は200または304になります)、Wing は更新が必要なファイルしか取得しません
    • ApplicationCacheは、HTML自体をキャッシュしてしまうためページ遷移型のアプリでは利用できませんが、Wingはページ遷移型のアプリにも導入可能です
  • ETag ヘッダ
    • ETag はキャッシュが存在する場合にデータの転送量がゼロになりますが、通信自体は発生してしまいます、Wing は通信自体を行いません
    • ETag は負荷分散構成のサーバでは使えません
  • Expires ヘッダ
    • Expires はキャッシュ期間中にブラウザからサーバへの更新確認のための通信を抑止できますが、サーバ上でファイルを更新してもクライアント側は更新されません(ファイル名を変更する必要があります)。Wing を使うと通信を抑止するだけではなく柔軟な運用が可能になります
    • Expires は、ずっと変化しない事が前提の静的なアセットをキャッシュするための機能ですが、Wingは頻繁に更新されるアセットのキャッシュにも利用できます
  • CDN
    • CDN を使うとサーバにリクエストするたびに料金が発生しますが、Wing はキャッシュ後に通信を行いません

Wing と Zabuton の違いについて

Wing は Zabuton の機能縮小版(サブセット)です。Wing に以下の機能は実装されていません。

  • iframe を使った無限ストレージ機能
    • postMessage を使ったドメイン間通信の機能
  • メモリ上のキャッシュを一定時間でGCする機能
  • デバッグ用に実際にはキャッシュしないブラックホールストレージを選択する機能
  • UTF16で格納されるデータを特殊な方法でエンコードし、通常の2倍のデータを保存する機能(doubler)
  • Scene とテンプレート機能
  • マニフェストファイルの互換性
    • Zabuton用のマニフェストはWingで読み込めません、互換性がありません

Wing の簡単な使い方

  1. var wing = new Wing(options); でインスタンスを生成し
  2. wing.init(callback) で初期化とマニフェストの更新を行います
  3. wing.fetchAll(callback) でキャッシュを更新し
  4. wing.get(id, toDataURI) でキャッシュ済みのアセットを取り出します
// JSX

import "Wing.jsx";

class _Main {
    static function main(args: string[]):void {
        var options = {
                verbose:         false, // デバッグ用の log を出力する/しない
                cache_busting:   false, // XHRのブラウザキャッシュをOFFにする/しない
                manifest:        null,  // マニフェストオブジェクト or マニフェストファイルURL
                hook_url:        function(url:string):string { // アセットのURLを置換するフック関数の指定
                  //return "http://example.com/" + url;
                    return url;
                }
            }:Map.<variant>;

        var wing = new Wing(options);

        wing.init(function(err:Error) { // 初期化完了でコールバック
            wing.fetchAll(function(err:Error, id:Array.<string>):void { // キャッシュの追従完了でコールバック
                log "Wing.fetchAll: " + id.join(", ");
            });
        });
    }
}

wing.fetchAll() により、全てのキャッシュがオンメモリになります(最大10MBのメモリを使用します)。
wing.fetchAll() の中で、wing.get(id) を行うとキャッシュデータを取り出せます。

// JSX
wing.fetchAll(function(err:Error, id:Array.<string>):void {
    log wing.get("hoge.txt");       // DataURIヘッダなしでキャッシュを取りだす "hello world"
    log wing.get("piyo.png", true); // DataURIヘッダ付きでキャッシュを取り出す "data:image/png;base64,..."
});

Wing API

Wing#is

Wing#is(id:string):boolean は アセット(id)がマニフェストに存在する場合に true を返します。
false ならそのようなアセットidは存在しません。

Wing#has

Wing#has(id:string):boolean は アセット(id)が fetch 済み(データがメモリ上に載っている状態)なら true を返します。
false ならfetch する必要があります。

Wing#fetch

Wing#fetch(id:string, fetchedCallback:function(err:Error, id:string):void):void はアセット(id)をサーバから取得しクライアントストレージにキャッシュします。

アセットに対して以下の処理を行います。

  • 内容が古くなっている → 現在のキャッシュを捨て、サーバから再取得を行いキャッシュする
  • 追加されたアセット → サーバから取得を行いキャッシュする
  • 不要なアセット → 不要なキャッシュを捨てる

Wing#fetchAll

Wing#fetchAll(fetchedCallback:function(err:Error, id:Array.<string>):void):void は全てのアセットをサーバから取得しクライアントストレージにキャッシュします。

以下の処理を全てのアセットに対して行います。

  • 内容が古くなっている → 現在のキャッシュを捨て、サーバから再取得を行いキャッシュする
  • 追加されたアセット → サーバから取得を行いキャッシュする
  • 不要なアセット → 不要なキャッシュを捨てる

Wing#get

Wing#get(id:string, toDataURI:boolean = false):string は fetch 済みのアセット(id)を文字列として取り出します。
アセットがバイナリデータで、第2引数が true なら、文字列の先頭に data:MimeType;base64; を追加します。

Wing#clear

Wing#clear(callback:function(err:Error):void = null):void は、キャッシュをクリアします。

キャッシュクリアは、以下の2通りの方法でもできます

  1. ブラウザのUIから「データを削除(MobileSafari)」や「キャッシュの消去(chrome)」を選択します
  2. delete localStorage.__WING__ を行い DELETE FROM Wing SQLを実行します

キャッシュの再同期

意図的にキャッシュを再同期させるには、wing のインスタンを一度破棄し、再度生成してください。

// JSX

wing = null;
wing = new Wing(options);

マニフェストファイルフォーマット

Wing で使用するマニフェストファイルのフォーマットです。

{
    "dirs": {
        "img1": "assets/images/1/",
        "img2": "assets/images/2/",
        "txt1": "assets/text/"
    },
    "assets": {
        "16_00":["121213", 1,  2,  807,  "png",  "img1",  "16_00.png"],
        "16_01":["121213", 1,  2,  843,  "png",  "img1",  "16_01.png"],
        "16_30":["121213", 2,  2,  843,  "png",  "img2",  "16_30.png"],
        "a.txt":["124445", 2,  1,    5,  "text", "txt1",  "a.txt"]
    }
}
  • dirs はアセットを格納するディレクトリの指定です。{ keyword: directory, ... } で複数指定可能です
    • directory は末尾が / で終わるように指定してください
  • assets はアセットのリストです。アセットの情報を [hash, db, code, size, ext, dir, file] の配列で指定します
    • hash - MD5で生成した16進数文字列の先頭6文字です。手動で作成する場合は適当な6文字の数字を設定してください
    • db - キャッシュをどのDBに保存するかの指定です。1を指定すると LocalStorage に保存し、2を指定するとWebSQLに保存します。 0 を指定するとストレージに保存しません
    • code - キャッシュをストレージに保存する際のエンコードの指定です。 1 はそのまま保存します, 2 はBase64化した状態で保存します
    • size - アセットのファイルサイズです
    • ext - "js" や "css" などの拡張子です
    • dir - ディレクトリの指定です。dirs に存在する名前を指定します
    • file - ファイル名の指定です。dirs[dir] + file が実際のURLになります

マニフェストファイルの作成/更新方法

まずは、dirs を含んだマニフェストファイル(asset.manifest.json)をエディタで作成します。

この例では o/assets/asset.manifest.json を作成しています。

{
    "dirs": {
        "img1": "o/assets/images/1/",
        "img2": "o/assets/images/2/",
        "txt": "o/assets/text/"
    },
}

次に、node toast.js -v o/assets/asset.manifest.json を実行します。
node toast.js コマンドは dirsに指定したディレクトリ直下のファイルを検索し、見つかったファイルを assets にリストアップします。

{
    "dirs": {
        "img1": "o/assets/images/1/",
        "img2": "o/assets/images/2/",
        "txt": "o/assets/text/"
    },
    "assets": {
        "16_00.png": [  "c10c96",  1,  2,  807,  "png",  "img1",  "16_00.png" ],
        "16_01.png": [  "782602",  1,  2,  843,  "png",  "img1",  "16_01.png" ],
        "16_02.png": [  "5a1f07",  1,  2,  841,  "png",  "img1",  "16_02.png" ],
        "16_03.png": [  "b024dc",  1,  2,  834,  "png",  "img1",  "16_03.png" ],
        "16_04.png": [  "b1c6c9",  1,  2,  849,  "png",  "img1",  "16_04.png" ],
        "16_10.png": [  "f7f8c7",  1,  2,  764,  "png",  "img1",  "16_10.png" ],
        "16_11.png": [  "201a89",  1,  2,  765,  "png",  "img1",  "16_11.png" ],
        "16_12.png": [  "402cb5",  1,  2,  754,  "png",  "img1",  "16_12.png" ],
        "16_13.png": [  "db226c",  1,  2,  767,  "png",  "img1",  "16_13.png" ],
        "16_14.png": [  "0efc53",  1,  2,  725,  "png",  "img1",  "16_14.png" ],
        "16_20.png": [  "0e57c2",  1,  2,  682,  "png",  "img1",  "16_20.png" ],
        "16_21.png": [  "1c5959",  1,  2,  679,  "png",  "img1",  "16_21.png" ],
        "16_22.png": [  "383b77",  1,  2,  684,  "png",  "img1",  "16_22.png" ],
        "16_23.png": [  "b2ee66",  1,  2,  708,  "png",  "img1",  "16_23.png" ],
        "16_24.png": [  "8d6c6b",  1,  2,  602,  "png",  "img1",  "16_24.png" ],
        "16_30.png": [  "b62bec",  1,  2,  756,  "png",  "img2",  "16_30.png" ],
        "16_31.png": [  "437b1a",  1,  2,  762,  "png",  "img2",  "16_31.png" ],
        "16_32.png": [  "5da4b2",  1,  2,  761,  "png",  "img2",  "16_32.png" ],
        "16_33.png": [  "b5a7b0",  1,  2,  760,  "png",  "img2",  "16_33.png" ],
        "16_34.png": [  "2a1580",  1,  2,  724,  "png",  "img2",  "16_34.png" ],
        "16_40.png": [  "978dd3",  1,  2,  748,  "png",  "img2",  "16_40.png" ],
        "16_41.png": [  "0d2185",  1,  2,  758,  "png",  "img2",  "16_41.png" ],
        "16_42.png": [  "b24dd8",  1,  2,  760,  "png",  "img2",  "16_42.png" ],
        "16_43.png": [  "f6ccd6",  1,  2,  752,  "png",  "img2",  "16_43.png" ],
        "16_44.png": [  "183d99",  1,  2,  725,  "png",  "img2",  "16_44.png" ],
        "16_50.png": [  "8ff6b0",  1,  2,  592,  "png",  "img2",  "16_50.png" ],
        "16_51.png": [  "690bdd",  1,  2,  610,  "png",  "img2",  "16_51.png" ],
        "16_52.png": [  "197751",  1,  2,  601,  "png",  "img2",  "16_52.png" ],
        "16_53.png": [  "76b19f",  1,  2,  620,  "png",  "img2",  "16_53.png" ],
        "16_54.png": [  "c23995",  1,  2,  577,  "png",  "img2",  "16_54.png" ],
        "a.txt": [  "dc9f32",  1,  1,  7,  "txt",  "txt",  "a.txt" ]
    }
}

注意: node toast.js -v o/assets/asset.manifest.json コマンドは実行するたびに、assets: { ... } を再作成します。


toast.js

toast.js は マニフェストファイルの assets 部分の更新を行うツールです。

node toast.js -v ./assets/asset.manifest.json のように使用します。
詳しくは node toast.js --help を見てください。