Кэширование jQuery ajax-ответа в javascript/браузере

Я хотел бы включить кеширование ответа ajax в javascript/browser.

Из jquery.ajax docs:

По умолчанию запросы всегда выдаются, но браузер может служить результаты из кеша. Чтобы запретить использование кэшированных результатов, установите кеш к false. Чтобы заставить запрос сообщить об ошибке, если актив не был изменен с момента последнего запроса, установите ifModified в true.

Однако ни один из этих адресов не вызывает кэширование.

Мотивация: Я хочу поместить вызовы $.ajax({...}) в мои инициализационные функции, некоторые из которых запрашивают один и тот же URL-адрес. Иногда мне нужно вызвать одну из этих функций инициализации, иногда я вызываю несколько.

Итак, я хочу свести к минимуму запросы на сервер, если этот URL уже загружен.

Я мог бы свернуть свое решение (с некоторым трудом!), но я хотел бы знать, есть ли стандартный способ сделать это.

Ответ 1

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

Рабочая скрипка здесь

EDIT: поскольку этот пост становится популярным, вот еще лучший ответ для тех, кто хочет управлять тайм-аутом таймаута, и вам также не нужно беспокоиться о всем беспорядке в $. 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);
}

И скрипка здесь ОСТОРОЖНО, не работая с $.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
            });
        });
    });
});

СКАЖИТЕ ЗДЕСЬ Некоторые проблемы, наш идентификатор кэша зависит от представления объекта json2 lib JSON.

Используйте консольное представление (F12) или FireBug для просмотра некоторых журналов, сгенерированных кешем.

Ответ 2

Я искал кеширование для своего хранилища приложений для мобильного телефона, и я нашел ответ @TecHunter, который отлично, но с помощью localCache.

Я нашел и узнал, что localStorage - еще одна альтернатива кешированию данных, возвращаемых вызовом ajax. Итак, я создал одну демонстрацию с помощью localStorage, которая поможет другим пользователям, которые могут захотеть использовать localStorage вместо localCache для кеширования.

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

Чтобы сохранить данные в 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");

Надеюсь, это поможет другим!

Ответ 3

Все современные браузеры предоставляют вам apis для хранения. Вы можете использовать их (localStorage или sessionStorage) для сохранения ваших данных.

Все, что вам нужно сделать, это после получения ответа хранить его в хранилище браузера. Затем в следующий раз, когда вы найдете тот же вызов, выполните поиск, если ответ уже сохранен. Если да, верните ответ оттуда; если не сделать новый звонок.

Smartjax плагин также выполняет похожие действия; но поскольку ваше требование просто сохраняет ответ на вызов, вы можете написать свой код внутри вашей функции jQuery ajax success, чтобы сохранить ответ. И перед вызовом просто проверьте, сохранен ли ответ.

Ответ 4

Если я понял ваш вопрос, вот решение:

    $.ajaxSetup({ cache: true});

и для конкретных вызовов

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

Если вы хотите наоборот (кэш для конкретных вызовов), вы можете установить false в начале и true для определенных вызовов.

Ответ 5

Старый вопрос, но мое решение немного другое.

Я писал одностраничное веб-приложение, которое постоянно вызывало вызовы ajax, вызванные пользователем, и для еще более затруднения требовались библиотеки, которые использовали методы, отличные от jquery (например, dojo, native xhr и т.д.). Я написал плагин для одного из моих собственных библиотек для максимально возможного кэширования запросов ajax таким образом, чтобы он работал во всех основных браузерах, независимо от того, из которых библиотеки использовались для вызова ajax.

В решении используется jSQL (написанный мной - постоянная реализация SQL на стороне клиента, написанная в javascript, которая использует indexeddb и другое dom методы хранения), и в комплекте с другой библиотекой, называемой XHRCreep (написанной мной), которая является полной перезаписью собственного XHR объект.

Чтобы реализовать все, что вам нужно сделать, это включить плагин на вашей странице, который здесь.

Есть два варианта:

jSQL.xhrCache.max_time = 60;

Установите максимальный возраст в минутах. все кэшированные ответы, которые старше этого, запрашиваются повторно. Значение по умолчанию - 1 час.

jSQL.xhrCache.logging = true;

Если установлено значение true, для отладки на консоли будут показаны ложные вызовы XHR.

Вы можете очистить кеш на любой странице через

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