Zwischenspeichern einer jquery ajax-Antwort in Javascript / Browser


96

Ich möchte das Caching einer Ajax-Antwort in Javascript / Browser aktivieren.

Aus den Dokumenten von jquery.ajax :

Standardmäßig werden immer Anforderungen ausgegeben, aber der Browser kann Ergebnisse aus seinem Cache bereitstellen. Um die Verwendung der zwischengespeicherten Ergebnisse zu verbieten, setzen Sie den Cache auf false. Setzen Sie ifModified auf true, damit die Anforderung einen Fehler meldet, wenn das Asset seit der letzten Anforderung nicht geändert wurde.

Keine dieser Adressen erzwingt jedoch das Caching.

Motivation: Ich möchte $.ajax({...})Anrufe in meine Initialisierungsfunktionen einfügen, von denen einige dieselbe URL anfordern. Manchmal muss ich eine dieser Initialisierungsfunktionen aufrufen, manchmal rufe ich mehrere auf.

Daher möchte ich die Anforderungen an den Server minimieren, wenn diese bestimmte URL bereits geladen wurde.

Ich könnte meine eigene Lösung entwickeln (mit einigen Schwierigkeiten!), Aber ich würde gerne wissen, ob es eine Standardmethode dafür gibt.


Ich hätte nicht gedacht, dass es schwierig ist, zu verfolgen, welche URLs Sie bereits geladen haben, und die Ergebnisse in dieser Liste zu speichern. Anschließend können Sie Ihre URLs überprüfen, bevor Sie einen AJAX-Anruf tätigen. Voila - Sie haben Ihren eigenen Basis-Cache.

Sie können das Cache-Steuerelement hinzufügen und den Header zu Ihrer Antwort auf dem Server
ablaufen lassen

Antworten:


141

cache:true funktioniert nur mit GET- und HEAD-Anfrage.

Sie könnten Ihre eigene Lösung, wie Sie sagten, mit etwas in diese Richtung rollen:

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

Hier Geige arbeiten

BEARBEITEN: Da dieser Beitrag immer beliebter wird, ist hier eine noch bessere Antwort für diejenigen, die den Timeout-Cache verwalten möchten, und Sie müssen sich auch nicht um das ganze Durcheinander in $ .ajax () kümmern, da ich $ .ajaxPrefilter () verwende. . Jetzt {cache: true}reicht es aus, nur den Cache richtig zu behandeln:

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

Und die Geige hier SORGFÄLTIG, nicht mit $ .Deferred arbeiten

Hier ist eine funktionierende, aber fehlerhafte Implementierung, die mit verzögert arbeitet:

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

Geige HIER Bei einigen Problemen hängt unsere Cache-ID von der JSON-Objektdarstellung von json2 lib ab.

Verwenden Sie die Konsolenansicht (F12) oder FireBug, um einige vom Cache generierte Protokolle anzuzeigen.


Gibt es einen Grund, warum Sie die localCache.setFunktion zurückrufen ? Warum nicht einfach doSomehing(jqXHR)nach dem Einstellen des Caches?
Cammil

Ich bevorzuge es einfach so, damit ich nicht so etwas tun muss, doSomething(localCache.set(url,jqXHR));aber es ist nur eine
persönliche

2
Irgendwelche Vorschläge zur Verbesserung, um die Verwendung von $ .ajax als Versprechen zu unterstützen? Wenn Sie false from beforeSend zurückgeben, wird die Anforderung abgebrochen (wie es sollte), sodass $ .ajax (...). Doed (Funktion (Antwort) {...}). Fail (...) jetzt nicht mehr funktioniert, da fail eher aufgerufen wird als getan ... und ich würde sie lieber nicht alle umschreiben :(
franck102

2
@TecHunter Vielen Dank dafür. Drei weitere geringfügige Verbesserungen könnten vorgenommen werden. Erstens, wenn mehrere Anforderungen gleichzeitig an dieselbe Ressource gestellt werden, verfehlen alle den Cache. Um dies zu beheben, möchten Sie möglicherweise den Cache für eine bestimmte "ID" so einstellen, dass er bei der ersten Anforderung ansteht, und das Senden von Ergebnissen für nachfolgende Anforderungen verschieben, bis die erste zurückkommt. Zweitens möchten Sie möglicherweise das Fehlerergebnis einer Anforderung zwischenspeichern, damit alle Anforderungen für dieselbe Ressource dasselbe Ergebnis erhalten.
mjhm

2
@TecHunter - Schöne Lösung, ich habe diese Lösung mit einer wichtigen Änderung verwendet. Wenn das zwischengespeicherte Objekt in anderen Funktionen geändert wird, führt dies zu Problemen. Während des Festlegens und Abrufens des zwischengespeicherten Objekts gebe ich eine Kopie dieses Objekts zurück, die wie unten gezeigt ist: localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Bharat Patil

11

Ich suchte nach Caching für meinen Phonegap-App-Speicher und fand die Antwort von @TecHunter, die großartig ist, aber fertig ist localCache.

Ich habe festgestellt und erfahren, dass localStorage eine weitere Alternative zum Zwischenspeichern der vom Ajax-Aufruf zurückgegebenen Daten ist. Also habe ich eine Demo erstellt, localStoragedie anderen hilft, die sie möglicherweise localStorageanstelle des localCacheCachings verwenden möchten .

Ajax Call:

$.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!");
    }
});

So speichern Sie Daten in 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
    };
}
});

Wenn Sie localStorage entfernen möchten, verwenden Sie die folgende Anweisung, wo immer Sie möchten:

localStorage.removeItem("Info");

Hoffe es hilft anderen!


Hallo, in localStorage.removeItem("Info");, über das "info"ist es die URL?
vsogrimen

@vsogrimen infoist das Objekt zum Speichern der Daten in localStorage.
immayankmodi

1
immer weiter responseJSON is not defined. Wie kann man das beheben? (Mein Datentyp ist HTML)
Nikita 6

@CreativeMind, entfernen Sie reponseJOSN und verwenden Sie "var responseData = JSON.stringify (data);"
Machen Sie

Ich bevorzuge den localStorage, da ich einen Cache für mehrere Anforderungen benötigte.
KeitelDOG

9

Alle modernen Browser bieten Ihnen Speicher-APIs. Sie können sie (localStorage oder sessionStorage) verwenden, um Ihre Daten zu speichern.

Alles, was Sie tun müssen, ist, nach Erhalt der Antwort diese im Browser-Speicher zu speichern. Wenn Sie das nächste Mal denselben Anruf finden, suchen Sie, ob die Antwort bereits gespeichert ist. Wenn ja, geben Sie die Antwort von dort zurück. Wenn nicht, tätigen Sie einen neuen Anruf.

Das Smartjax- Plugin macht ähnliche Dinge; Da Sie jedoch nur die Anrufantwort speichern müssen, können Sie Ihren Code in Ihre jQuery Ajax-Erfolgsfunktion schreiben, um die Antwort zu speichern. Und bevor Sie einen Anruf tätigen, überprüfen Sie einfach, ob die Antwort bereits gespeichert ist.


Ich habe Antworten in IndexedDB gespeichert. Gibt es eine Möglichkeit, IndexedDB zu überprüfen? Wenn ich den Sitzungsspeicher verwende, gibt es auch eine Möglichkeit, mithilfe von jQuery zu überprüfen, ob eine Antwort vorhanden ist oder nicht. Ich kann keine andere Bibliothek als jQuery einbinden. Danke,
Abhishek Aggarwal

7

Wenn ich Ihre Frage verstanden habe, ist hier die Lösung:

    $.ajaxSetup({ cache: true});

und für bestimmte Anrufe

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

Wenn Sie das Gegenteil wünschen (Cache für bestimmte Anrufe), können Sie am Anfang false und true für bestimmte Anrufe festlegen.


2
das ist genau das Gegenteil
TecHunter

Es funktioniert bei mir, danke! Es ist seltsam, dass das Caching auf meiner Seite nicht standardmäßig aktiviert war.
Timur Nurlygayanov

2

Alte Frage, aber meine Lösung ist etwas anders.

Ich habe eine einseitige Web-App geschrieben, die ständig vom Benutzer ausgelöste Ajax-Aufrufe ausführte. Um dies noch schwieriger zu machen, waren Bibliotheken erforderlich, die andere Methoden als jquery verwendeten (wie Dojo, native xhr usw.). Ich habe ein Plugin für eine meiner eigenen Bibliotheken geschrieben, um Ajax-Anforderungen so effizient wie möglich zwischenzuspeichern, so dass es in allen gängigen Browsern funktioniert, unabhängig davon, welche Bibliotheken für den Ajax-Aufruf verwendet wurden.

Die Lösung verwendet jSQL (von mir geschrieben - eine clientseitige persistente SQL-Implementierung in Javascript, die indexeddb und andere dom-Speichermethoden verwendet) und wird mit einer anderen Bibliothek namens XHRCreep (von mir geschrieben) gebündelt, die vollständig neu geschrieben wird das native XHR-Objekt.

Um alles zu implementieren, was Sie tun müssen, fügen Sie das Plugin in Ihre Seite ein, die sich hier befindet .

Es gibt zwei Möglichkeiten:

jSQL.xhrCache.max_time = 60;

Stellen Sie das maximale Alter in Minuten ein. Alle zwischengespeicherten Antworten, die älter als diese sind, werden erneut angefordert. Standard ist 1 Stunde.

jSQL.xhrCache.logging = true;

Wenn true festgelegt ist, werden in der Konsole nachgebildete XHR-Aufrufe zum Debuggen angezeigt.

Sie können den Cache auf jeder Seite über löschen

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

Ich kann Ihr Plugin in Github und auf der offiziellen Website nicht finden. Bitte aktualisieren Sie Ihre Antwort. Ich brauche Ihr Plugin :) Ich bin ein Anhänger von Ihnen in Github. Gute Arbeit;) @ occams-razor
Alican Kablan

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

Geben Sie hier die Linkbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.