javascript / browserでのjquery ajax応答のキャッシュ


96

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

jquery.ajax docsから:

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

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

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

そのため、特定のURLが既に読み込まれている場合は、サーバーへの要求を最小限に抑えたいと思います。

私は自分のソリューションをロールバックすることもできますが(少し難しいですが)、これを行うための標準的な方法があるかどうか知りたいです。


既にロードしたURLを追跡し、そのリストに対して結果を保存するのは難しいとは思いませんでした。その後、AJAX呼び出しを行う前にURLを確認できます。出来上がり-あなたはあなた自身の基本的なキャッシュを持っています。

サーバーの応答にcache-controlとexpiresヘッダーを追加できるので、サーバーはその値で構成したタイムアウト後にのみ呼び出す必要があります
hereandnow78

回答:


141

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);
}

ここで作業フィドル

編集:この投稿が人気になると、タイムアウトキャッシュを管理したい人のためのさらに良い答えがここにあります。私は$ .ajaxPrefilter()を使用しているので、$。ajax()のすべての混乱に煩わされる必要もありません。これで、設定を行うだけで、キャッシュを正しく処理できます。{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);
}

そして、ここでのフィドル は$ .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
            });
        });
    });
});

ここでフィドル いくつかの問題、私たちのキャッシュIDはjson2 lib JSONオブジェクト表現に依存しています。

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


localCache.set関数にコールバックを配置する理由はありますか?なぜdoSomehing(jqXHR)キャッシュを設定した後ではないのですか?
cammil 2013年

私はこの方法を好むだけなので、次のようなことをする必要はありませんがdoSomething(localCache.set(url,jqXHR));、それは
個人

2
$ .ajaxを約束として使用するためにこれを改善するための提案はありますか?beforeSendからfalseを返すとリクエストがキャンセルされ(必要に応じて)、既存の$ .ajax(...)。done(function(response){...})。fail(...)が失敗します。完了よりも...そして私はむしろそれらすべてを書き直し
たく

2
@TecHunterこれをありがとう。さらに3つの改善を加えることができます。まず、同じリソースに対して複数のリクエストが同時に行われた場合、それらはすべてキャッシュをミスします。これを修正するには、特定の「ID」のキャッシュを最初のリクエストで保留に設定し、最初のリクエストが返ってくるまで後続のリクエストの送信結果を延期することができます。次に、同じリソースに対するすべてのリクエストが同じ結果になるように、リクエストのエラー結果をキャッシュすることができます。
mjhm 2014年

2
@TecHunter-素晴らしい解決策、私はこの解決策を1つの重要な変更とともに使用しました。:キャッシュされたオブジェクトは、他の機能に変更されている場合、それはそう、キャッシュされたオブジェクトを設定し、取得中に、私は以下のように示されているオブジェクトのコピーを返すよ、問題が発生します localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
バーラトパティル

11

私の電話ギャップアプリストレージのキャッシュを探していたところ、@ TecHunterの答えを見つけましたlocalCache

localStorageがajax呼び出しによって返されたデータをキャッシュするもう1つの代替手段であることを知り、知りました。それで、私はキャッシングの代わりlocalStorageに使用したいと思うかもしれない他の人を助けるであろうを使って1つのデモを作成しました。localStoragelocalCache

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");

それが他の人を助けることを願っています!


こんにちは、のURL localStorage.removeItem("Info");について "info"
vsogrimen 2016年

@vsogrimen infoは、データをlocalStorageに格納するオブジェクトです。
immayankmodi 2016年

1
取得し続けるresponseJSON is not defined。これを修正する方法はありますか?(私のデータ型はhtmlです)
Nikita웃2016

@ CreativeMind、reponseJOSNを削除、「var responseData = JSON.stringify(data);」を使用 代わりに、success(data)で同じことを行います
Unicco 2017

私は複数のリクエストにわたってキャッシュが必要だったので、localStorageを好みます。
KeitelDOG

9

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

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

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


IndexedDBに応答を保存しましたが、IndexedDBを確認する方法はありますか?また、セッションストレージを使用する場合、jQueryを使用して応答が存在するかどうかを確認する方法があります。jQuery以外のライブラリを含めることはできません。ありがとう、
Abhishek Aggarwal

7

私があなたの質問を理解したなら、これが解決策です:

    $.ajaxSetup({ cache: true});

そして、特定の呼び出しのために

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

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


2
それは正反対です
TecHunter

それは私のために働きます、ありがとう!キャッシングが私のページではデフォルトで有効になっていなかったという奇妙な..です
ティムールNurlygayanov

2

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

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

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

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

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

jSQL.xhrCache.max_time = 60;

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

jSQL.xhrCache.logging = true;

trueに設定すると、モックXHR呼び出しがデバッグのためにコンソールに表示されます。

あなたは与えられたページのキャッシュをクリアすることができます

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

githubと公式ウェブサイトでプラグインが見つかりません。プラグインが必要です。良い仕事;)@ occams-razor
Alican Kablan 2018

-1
        function getDatas() {
            let cacheKey = 'memories';

            if (cacheKey in localStorage) {
                let datas = JSON.parse(localStorage.getItem(cacheKey));

                // if expired
                if (datas['expires'] < Date.now()) {
                    localStorage.removeItem(cacheKey);

                    getDatas()
                } else {
                    setDatas(datas);
                }
            } else {
                $.ajax({
                    "dataType": "json",
                    "success": function(datas, textStatus, jqXHR) {
                        let today = new Date();

                        datas['expires'] = today.setDate(today.getDate() + 7) // expires in next 7 days

                        setDatas(datas);

                        localStorage.setItem(cacheKey, JSON.stringify(datas));
                    },
                    "url": "http://localhost/phunsanit/snippets/PHP/json.json_encode.php",
                });
            }
        }

        function setDatas(datas) {
            // display json as text
            $('#datasA').text(JSON.stringify(datas));

            // your code here
           ....

        }

        // call
        getDatas();

ここにリンクの説明を入力

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.