H 5の「オフラインアプリケーション」


H 5の「オフラインメモリ」
「オフラインストレージ」:名前の通り、有線環境でデータ(静的リソース、動的リソースを含む)をキャッシュし、オフライン環境でもアプリケーションを正常に使用できます(単一ページアプリケーション)
静的リソースストレージ(ApplicationCache)
アプリケーションCacheはh 5静的資源キャッシュ方式である.この技術により、静的リソース/転送要求の構成を実現する、アプリケーションのロード速度を速め、サーバの負荷を低減することができる.
基本的な使い方
  • manifestプロファイル
  • を導入
    
    
        
            ...
        
        
            ...
        
    
  • manifestファイル
  • の構成
    CACHE MANIFEST
    #      ,       js  ,      
    # 2016972143
    #   :       ,      ,       
    CACHE:
    /dist/0.eda078350ef514670764.bundle.js
    /dist/common.bundle.js?v=2016972143
    /dist/df9f379beae2559b27044dcfdc0653ab.png?v=2016972143
    /dist/home.bundle.js?v=2016972143
    /dist/home.css?v=2016972143
    uncached.js?v=2016972143
    
    #cached.css
    
    #   :      ,         ,     
    NETWORK:
    *
    #uncached.js
    #uncached.css
    
    #   :            , index.html    ,   404  
    FALLBACK:
    #/v1/team/dirlists mock/team_dirlists.json
    #/v1/team/app_filelist?isAdd=0&source=team&page=1&pageSize=10&sort=ftime&from=hiwebapp&fid=t293 mock/team_app_filelist.json
    #index.html 404.html
  • 書き込み更新バッファjs
  • //             ,    
    // !!!   :           ,        
    (function () {
        var cache = window.applicationCache;
    
        cache.addEventListener('updateready', function(e) {
            if (cache.status == cache.UPDATEREADY) {
                // Browser downloaded a new app cache.
                // if (confirm('A new version of this site is available. Load it?')) {
                    cache.swapCache();
                    window.location.reload();
                // }
            } else {
                // Manifest didn't changed. Nothing new to server.
            }
        }, false);
    
        cache.update()
    
    }())
  • サーバ構成
  • manifestファイルを構成し、Content-Type: text/cache-manifest Cache-Control: max-age=0
  • に応答する.
  • オンラインコードの配備時にmanifestバージョン番号と構成
  • を更新
    以上の構成により、静的リソースキャッシュを実現することができる.
    上記の図のように、from cacheのロード時間は他のネットワークリクエストよりずっと速い!このうちfetch/ajaxの要求は、応答結果が変化する可能性があるため、静的リソースによって記憶できない.
    では、非同期ajaxリクエスト(ダイナミックリソース)はどのような方法で格納できますか?本格的なオフラインストレージを実現する.
    動的リソースストレージ(WebSQL/IndexedDB)
    フロントエンドデータベースを使用すると、ダイナミックリソースストレージを柔軟に制御できます.ここではindexedDBを使用していますが、なぜWebSQLを使用しないのですか?
  • 以前にオンラインチャットアプリケーションをした時、WebSQLを使ってチャット記録
  • を保存したことがある.
  • WebSQLはすでに廃棄されています
  • WebSQLは従来のリレーショナル・データベースであり、indexedDBは主流のNoSQL DB
  • である.
    基本的な使い方
  • 汎用データベースアクセスインタフェース
  • を作成する
    var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
    
    // memCache     ,          
    var req, db, memCache = {};
    if(indexedDB) {
        // version:2
        req = indexedDB.open('ajax_cache', 2);
        //   caches    
        req.onsuccess = function (e) {
            db = e.target.result;
            if(!db.objectStoreNames.contains('caches')){
                db.createObjectStore('caches', {keyPath: "id"});
            }
        }
        //          
        req.onupgradeneeded=function(e){
            var db=e.target.result;
            if(!db.objectStoreNames.contains('caches')){
                db.createObjectStore('caches', {keyPath: "id"});
            }
            console.log('DB version changed to ' + db.version);
        };
        req.onerror = function (err) {
            console.error('indexedDB open failed. ', err)
        }
    }
    
    export default {
        isSupported: !!indexedDB,
        set: (id, data) => {
            var entity = {
                id: id,
                data: data
            }
            var transaction = db.transaction('caches', 'readwrite');
            var store = transaction.objectStore('caches');
            var req = store.put(entity);
            req.onerror = () => {
                console.error('put data failed. ', entity)
            }
            req.onsuccess = () => {
                memCache[id] = data
                console.info('put data successed. ', entity)
            }
        },
        get: (id) => {
            return new Promise((resolve, reject) => {
                if(memCache[id]) {
                    resolve(memCache[id]);
                    return;
                }
    
                var transaction = db.transaction('caches', 'readwrite');
                var store = transaction.objectStore('caches');
                var req = store.get(id);
                req.onerror = () => {
                    console.error('get data failed. ', id)
                    resolve()
                }
                req.onsuccess = (e) => {
                    var rlt = e.target.result;
                    console.info('get data successed. ', id, rlt)
                    resolve(rlt && rlt.data)
                }
            })
        }
    }
  • fetch/ajaxメソッド
  • を書き換える
    /* reset fetch function for offline be compatible*/
    var fetch = require('isomorphic-fetch')
    import {parse} from 'url'
    
    var __fetch = fetch;
    fetch = function (url) {
        var rlt = parse(url, true);
        function generateJson(json) {
            return {
                json: function () {
                    return json
                }
            }
        }
        function generateErrorJson() {
            return generateJson({
                errno: 500, errmsg: '        ',
                result: {
                    files: []
                }
            })
        }
        var query = rlt.query;
        //          from  
        delete query.t;
        delete query.from;
        var id = rlt.pathname
        var key = MyUtils.jsonToUrl(query)
        if(MyUtils.isOffline()) { //   
            if(!id) {
                return new Promise((resolve, reject) => {
                    resolve(generateErrorJson())
                })
            } else {
                if(DB.isSupported) {
                    return DB.get(id).then(json => {
                        return (!json || !json[key])
                            ? generateErrorJson()
                            : generateJson(json[key])
                    })
                } else {
                    return new Promise((resolve, reject) => {
                        resolve(generateErrorJson())
                    })
                }
            }
        } else {
            return __fetch.apply(null, [].slice.call(arguments))
                .then(res => res.json())
                .then( (resJson) => {
                    if(DB.isSupported) {
                        var tmp = {};
                        tmp[key] = resJson;
                        DB.get(id).then(json => {
                            DB.set(id, Object.assign({}, json, tmp))
                        })
                    }
                    return generateJson(resJson)
                }
            )
        }
    
    }

    chromeのweb toolでindexedDBを見ることができます
    リクエストのたびにキャッシュされます
    ネットを離れた後!非同期リクエストをシミュレートすることもできます!