web-dev-qa-db-ja.com

javascript / browserでjquery ajax応答をキャッシュする

Javascript/browserでajax応答のキャッシュを有効にしたいと思います。

jquery.ajax docs から:

デフォルトでは、リクエストは常に発行されますが、ブラウザはキャッシュから結果を提供する場合があります。キャッシュされた結果の使用を禁止するには、キャッシュをfalseに設定します。アセットが最後のリクエスト以降に変更されていない場合、リクエストに失敗を報告させるには、ifModifiedをtrueに設定します。

ただし、これらのアドレスはどちらもキャッシュを強制しません。

動機:私は初期化関数に$.ajax({...})呼び出しを入れたいです。そのいくつかは同じURLを要求します。これらの初期化関数の1つを呼び出す必要がある場合もあれば、いくつか呼び出す場合もあります。

だから、その特定のURLがすでにロードされている場合、サーバーへのリクエストを最小限にしたいです

独自のソリューションを展開することもできますが(多少の困難を伴います!)、これを行う標準的な方法があるかどうかを知りたいです。

86
cammil

cache:trueは、GETおよびHEAD要求でのみ機能します。

あなたがこれらの線に沿って何かで言ったように、あなたはあなた自身の解決を転がすことができました:

var localCache = {
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return localCache.data.hasOwnProperty(url) && localCache.data[url] !== null;
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url];
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = cachedData;
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true,
            beforeSend: function () {
                if (localCache.exist(url)) {
                    doSomething(localCache.get(url));
                    return false;
                }
                return true;
            },
            complete: function (jqXHR, textStatus) {
                localCache.set(url, jqXHR, doSomething);
            }
        });
    });
});

function doSomething(data) {
    console.log(data);
}

ここで作業フィドル

編集:この投稿が人気になると、タイムアウトキャッシュを管理したい人のためのより良い答えがあります。また、 $。ajax() のすべての混乱を気にする必要はありません $。ajaxPrefilter() を使用します。キャッシュを正しく処理するには、{cache: true}を設定するだけで十分です。

var localCache = {
    /**
     * timeout for cache in millis
     * @type {number}
     */
    timeout: 30000,
    /** 
     * @type {{_: number, data: {}}}
     **/
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        var complete = originalOptions.complete || $.noop,
            url = originalOptions.url;
        //remove jQuery cache as we have our own localCache
        options.cache = false;
        options.beforeSend = function () {
            if (localCache.exist(url)) {
                complete(localCache.get(url));
                return false;
            }
            return true;
        };
        options.complete = function (data, textStatus) {
            localCache.set(url, data, complete);
        };
    }
});

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true,
            complete: doSomething
        });
    });
});

function doSomething(data) {
    console.log(data);
}

そしてここでのフィドルCAREFUL、$。Deferredで動作しない

以下は、deferredで動作する、動作しているが欠陥のある実装です。

var localCache = {
    /**
     * timeout for cache in millis
     * @type {number}
     */
    timeout: 30000,
    /** 
     * @type {{_: number, data: {}}}
     **/
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        //Here is our identifier for the cache. Maybe have a better, safer ID (it depends on the object string representation here) ?
        // on $.ajax call we could also set an ID in originalOptions
        var id = originalOptions.url+ JSON.stringify(originalOptions.data);
        options.cache = false;
        options.beforeSend = function () {
            if (!localCache.exist(id)) {
                jqXHR.promise().done(function (data, textStatus) {
                    localCache.set(id, data);
                });
            }
            return true;
        };

    }
});

$.ajaxTransport("+*", function (options, originalOptions, jqXHR, headers, completeCallback) {

    //same here, careful because options.url has already been through jQuery processing
    var id = originalOptions.url+ JSON.stringify(originalOptions.data);

    options.cache = false;

    if (localCache.exist(id)) {
        return {
            send: function (headers, completeCallback) {
                completeCallback(200, "OK", localCache.get(id));
            },
            abort: function () {
                /* abort code, nothing needed here I guess... */
            }
        };
    }
});

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true
        }).done(function (data, status, jq) {
            console.debug({
                data: data,
                status: status,
                jqXHR: jq
            });
        });
    });
});

Fiddle HERE いくつかの問題、キャッシュIDはjson2 lib JSONオブジェクト表現に依存しています。

コンソールビュー(F12)またはFireBugを使用して、キャッシュによって生成されたログを表示します。

132
TecHunter

私はphonegapアプリストレージのキャッシュを探していましたが、@ TecHunterの答えを見つけました。これは素晴らしいですが、localCacheを使用して完了しました。

LocalStorageは、ajax呼び出しによって返されたデータをキャッシュする別の代替手段であることがわかりました。そこで、キャッシュにlocalStorageの代わりにlocalStorageを使用したい人に役立つlocalCacheを使用して1つのデモを作成しました。

Ajax呼び出し:

$.ajax({
    type: "POST",
    dataType: 'json',
    contentType: "application/json; charset=utf-8",
    url: url,
    data: '{"Id":"' + Id + '"}',
    cache: true, //It must "true" if you want to cache else "false"
    //async: false,
    success: function (data) {
        var resData = JSON.parse(data);
        var Info = resData.Info;
        if (Info) {
            customerName = Info.FirstName;
        }
    },
    error: function (xhr, textStatus, error) {
        alert("Error Happened!");
    }
});

データをlocalStorageに保存するには:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.cache) {
    var success = originalOptions.success || $.noop,
        url = originalOptions.url;

    options.cache = false; //remove jQuery cache as we have our own localStorage
    options.beforeSend = function () {
        if (localStorage.getItem(url)) {
            success(localStorage.getItem(url));
            return false;
        }
        return true;
    };
    options.success = function (data, textStatus) {
        var responseData = JSON.stringify(data.responseJSON);
        localStorage.setItem(url, responseData);
        if ($.isFunction(success)) success(responseJSON); //call back to original ajax call
    };
}
});

LocalStorageを削除する場合は、次のステートメントを必要な場所で使用します。

localStorage.removeItem("Info");

他の人の役に立つことを願っています!

10
Mayank Modi

最新のブラウザはすべて、ストレージAPIを提供します。それらを使用して(localStorageまたはsessionStorage)データを保存できます。

応答を受信した後、ブラウザストレージに保存するだけです。次に、同じ呼び出しを見つけたときに、応答が既に保存されているかどうかを検索します。はいの場合、そこから応答を返します。新たに電話をかけない場合。

Smartjax プラグインも同様のことを行います。ただし、呼び出し応答を保存するだけなので、jQuery ajax成功関数内にコードを記述して応答を保存できます。呼び出しを行う前に、応答が既に保存されているかどうかを確認してください。

8
Paul Shan

あなたの質問を理解したなら、ここに解決策があります:

    $.ajaxSetup({ cache: true});

特定の通話

 $.ajax({
        url: ...,
        type: "GET",
        cache: false,           
        ...
    });

反対(特定の呼び出しのキャッシュ)が必要な場合は、最初にfalseを設定し、特定の呼び出しにtrueを設定できます。

5
spring

古い質問ですが、私の解決策は少し異なります。

私は常にユーザーによってトリガーされるajax呼び出しを行う単一ページのWebアプリを作成していましたが、さらに難しくするために、jquery以外のメソッド(dojo、ネイティブxhrなど)を使用するライブラリが必要でした。 my own libraries のいずれかのプラグインを作成し、ajax呼び出しの実行に使用されているライブラリに関係なく、すべての主要なブラウザーで機能する方法でajaxリクエストを可能な限り効率的にキャッシュします。

ソリューションは jSQL (私が作成-javascriptで記述され、indexeddbおよびその他のdomストレージメソッドを使用するクライアント側永続SQL実装)を使用し、 XHRCreep と呼ばれる別のライブラリにバンドルされています=(私が書いた)これは、ネイティブXHRオブジェクトの完全な書き直しです。

必要なことをすべて実装するには、ページにプラグイン こちら を含めます。

次の2つのオプションがあります。

jSQL.xhrCache.max_time = 60;

最大経過時間を分単位で設定します。これより古いキャッシュされた応答はすべて再要求されます。デフォルトは1時間です。

jSQL.xhrCache.logging = true;

Trueに設定すると、デバッグ用に模擬XHR呼び出しがコンソールに表示されます。

特定のページのキャッシュは次の方法でクリアできます

jSQL.tables = {}; jSQL.persist();