web-dev-qa-db-fra.com

Mise en cache d'une réponse ajax jquery dans javascript / browser

Je souhaite activer la mise en cache d'une réponse ajax dans javascript/browser.

À partir de la documentation jquery.ajax :

Par défaut, les demandes sont toujours émises, mais le navigateur peut diffuser les résultats hors de son cache. Pour interdire l'utilisation des résultats mis en cache, définissez cache sur false. Pour que la demande rapporte l'échec si l'actif n'a pas été modifié depuis la dernière demande, définissez ifModified sur true.

Cependant, aucune de ces adresses ne force la mise en cache.

Motivation: Je souhaite placer des appels $.ajax({...}) dans mes fonctions d'initialisation, dont certaines demandent la même adresse URL. Parfois, j'ai besoin d'appeler l'une de ces fonctions d'initialisation, parfois, j'en appelle plusieurs.

Je souhaite donc minimiser les demandes adressées au serveur si cette URL particulière a déjà été chargée.

Je pourrais lancer ma propre solution (avec quelques difficultés!), Mais j'aimerais savoir s'il existe un moyen standard de le faire.

89
cammil

cache:true ne fonctionne qu'avec les requêtes GET et HEAD.

Comme vous l'avez dit, vous pouvez lancer votre propre solution en procédant comme suit:

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

Travaillez du violon ici

EDIT: à mesure que ce message devient populaire, voici une réponse encore meilleure pour ceux qui souhaitent gérer le cache de dépassement de délai et vous n'avez pas non plus à vous soucier de tout le fouillis du $. Ajax () J'utilise $. AjaxPrefilter () . Maintenant, il suffit de définir {cache: true} pour gérer correctement le cache:

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

Et le violon ici ATTENTION, ne fonctionne pas avec $ .Deferred

Voici une implémentation qui fonctionne, mais qui présente des défauts, et qui est différée:

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 Quelques problèmes, notre identifiant de cache dépend de la représentation de l'objet JSON json2 lib.

Utilisez la vue Console (F12) ou FireBug pour afficher certains journaux générés par le cache.

135
TecHunter

Je cherchais la mise en cache pour le stockage de mon application phonegap et j'ai trouvé la réponse de @TecHunter, ce qui est génial, mais effectué avec localCache.

J'ai trouvé et je sais que localStorage est une autre alternative pour mettre en cache les données renvoyées par un appel ajax. J'ai donc créé une démo en utilisant localStorage, ce qui aidera les autres utilisateurs potentiels à utiliser localStorage au lieu de localCache pour la mise en cache.

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

Pour stocker des données dans 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
    };
}
});

Si vous souhaitez supprimer localStorage, utilisez l'instruction suivante où vous le souhaitez:

localStorage.removeItem("Info");

J'espère que ça aide les autres!

10
Mayank Modi

Tous les navigateurs modernes vous fournissent des apis de stockage. Vous pouvez les utiliser (localStorage ou sessionStorage) pour enregistrer vos données.

Après réception de la réponse, stockez-la dans le stockage du navigateur. Ensuite, la prochaine fois que vous trouverez le même appel, recherchez si la réponse est déjà enregistrée. Si oui, retournez la réponse à partir de là; sinon faites un nouvel appel.

Smartjax le plugin fait aussi des choses similaires; mais comme vos exigences ne font que sauvegarder la réponse à un appel, vous pouvez écrire votre code dans votre fonction de réussite jQuery ajax pour enregistrer la réponse. Et avant de passer un appel, vérifiez si la réponse est déjà enregistrée.

8
Paul Shan

Si j'ai bien compris votre question, voici la solution:

    $.ajaxSetup({ cache: true});

et pour des appels spécifiques

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

Si vous voulez l’opposé (cache pour des appels spécifiques), vous pouvez définir la valeur false au début et la valeur true à des appels spécifiques.

5
spring

Vieille question, mais ma solution est un peu différente.

J'écrivais une application Web d'une seule page qui faisait constamment des appels ajax déclenchés par l'utilisateur. Pour rendre la tâche encore plus difficile, elle nécessitait des bibliothèques utilisant des méthodes autres que jquery (comme le dojo, le xhr natif, etc.). J'ai écrit un plugin pour l'un de mes propres bibliothèques afin de mettre en cache les requêtes ajax aussi efficacement que possible, de manière à fonctionner dans tous les principaux navigateurs, quelles que soient les bibliothèques utilisées pour passer l'appel ajax.

La solution utilise jSQL (écrite par moi - une implémentation SQL persistante côté client écrite en javascript qui utilise indexeddb et d'autres méthodes de stockage dom), et est fournie avec une autre bibliothèque appelée XHRCreep = (écrit par moi) qui est une réécriture complète de l'objet XHR natif.

Pour mettre en œuvre tout ce que vous avez à faire, vous devez inclure le plugin dans votre page, qui est ici .

Il y a deux options:

jSQL.xhrCache.max_time = 60;

Définissez l'âge maximum en minutes. toute réponse en cache plus ancienne que celle-ci est à nouveau demandée. La valeur par défaut est 1 heure.

jSQL.xhrCache.logging = true;

Lorsqu'il est défini sur true, les appels fictifs XHR sont affichés dans la console pour le débogage.

Vous pouvez vider le cache sur n'importe quelle page via

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