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


98

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

Из документов jquery.ajax :

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

Однако ни один из них не касается принудительного кэширования.

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

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

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


Я бы не подумал, что будет сложно отследить, какие URL-адреса вы уже загрузили, и сохранить результаты в этом списке. Затем вы можете проверить свои URL-адреса, прежде чем выполнять вызов AJAX. Вуаля - у вас есть собственный базовый кеш.

вы можете добавить заголовок cache-control и expires к вашему ответу на сервере, поэтому ваш сервер должен вызываться только после тайм-аута, который вы настроили в этих значениях
78

Ответы:


145

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

И скрипка здесь ОСТОРОЖНА, не работает с $ .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 Некоторые проблемы, наш идентификатор кеша зависит от представления объекта JSON json2 lib.

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


есть ли причина, по которой вы выполняете обратный вызов localCache.setфункции? Почему не просто doSomehing(jqXHR)после установки кеша?
cammil

Я просто предпочитаю так, поэтому мне не нужно делать что-то подобное, doSomething(localCache.set(url,jqXHR));но это просто личные предпочтения
TecHunter

2
Любые предложения по улучшению этого для поддержки использования $ .ajax в качестве обещания? Возврат false из beforeSend отменяет запрос (как и должен), поэтому существующие $ .ajax (...). Done (function (response) {...}). Fail (...) теперь перестают работать, потому что скорее всего вызывается ошибка чем сделано ... и я бы предпочел не переписывать их все :(
franck102 06

2
@TecHunter Большое спасибо за это. Можно было сделать еще три небольших улучшения. Во-первых, если к одному и тому же ресурсу одновременно поступает несколько запросов, все они будут пропускать кеш. Чтобы исправить это, вы можете установить кеш для определенного «id» в состояние ожидания с первым запросом и отложить отправку результатов для последующих запросов до тех пор, пока не вернется первый. Во-вторых, вы можете захотеть кэшировать результат ошибки запроса, чтобы все запросы одного и того же ресурса получали одинаковый результат.
mjhm

2
@TecHunter - Хорошее решение, я использовал это решение с одним важным изменением. Если кэшированный объект модифицируется в других функциях, это вызовет проблему, поэтому при установке и получении кэшированного объекта я возвращаю копию этого объекта, которая, как показано ниже: localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Бхарат Патил

12

Я искал кеширование для своего хранилища приложений для телефонных разговоров, и я нашел ответ @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");

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


Привет, localStorage.removeItem("Info");насчет "info"этого URL?
vsogrimen

@vsogrimen info- это объект для хранения данных в localStorage.
immayankmodi

1
продолжай получать responseJSON is not defined. Как это исправить? (мой тип данных html)
Никита 웃 06

@CreativeMind, удалите reponseJOSN и используйте «var responseData = JSON.stringify (data);» вместо этого сделайте то же самое с успехом (данные)
Unicco

Я предпочитаю localStorage, так как мне нужен кеш для нескольких запросов.
KeitelDOG

9

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

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

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


У меня есть ответы, сохраненные в IndexedDB, есть ли способ проверить IndexedDB? Кроме того, если я использую хранилище сеансов, есть ли способ проверить, присутствует ли ответ или нет, с помощью jQuery. Я не могу включить какую-либо другую библиотеку, кроме jQuery. Спасибо,
Абхишек Аггарвал

7

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

    $.ajaxSetup({ cache: true});

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

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

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


2
это с точностью до наоборот
TecHunter

У меня работает, спасибо! Странно, что на моей странице по умолчанию не было включено кеширование ..
Тимур Нурлыгаянов

2

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

Я писал одностраничное веб-приложение, которое постоянно выполняло вызовы 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();

Я не могу найти ваш плагин на github и на официальном сайте. Пожалуйста, обновите ваш ответ. Мне нужен ваш плагин :) Я ваш последователь в github. Хорошая работа;) @ occams-razor
Аликан Каблан

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

введите описание ссылки здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.