Кешування відповіді ajax jquery у javascript / браузері


96

Я хотів би включити кешування відповіді ajax в javascript / браузері.

З документів jquery.ajax :

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

Однак жодне з них не примушує кешувати.

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

Отже, я хочу мінімізувати запити до сервера, якщо ця конкретна URL-адреса вже завантажена.

Я міг би запропонувати своє власне рішення (з певними труднощами!), Але я хотів би знати, чи існує стандартний спосіб зробити це.


Я б не подумав, що складно відстежувати, які URL-адреси ви вже завантажили, і зберігати результати у цьому списку. Потім ви можете перевірити свої URL-адреси перед тим, як зробити дзвінок AJAX. Вуаля - у вас є власний базовий кеш.

Ви можете додати кеш-контроль та заголовок закінчується до своєї відповіді на сервері, тому ваш сервер повинен викликатися лише після тайм-ауту, який ви налаштували в цих значеннях
hereandnow78

Відповіді:


142

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

Скрипати ТУТ Деякі проблеми, ідентифікатор нашого кешу залежить від представлення об’єкта JSON json2 lib.

Використовуйте Console view (F12) або FireBug, щоб переглянути деякі журнали, створені кешем.


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

Я просто віддаю перевагу цьому, тому мені не потрібно робити щось подібне, doSomething(localCache.set(url,jqXHR));але це лише
особисті

2
Будь-яка пропозиція щодо вдосконалення цього для підтримки використання $ .ajax як обіцянки? Повернення false з beforeSend скасовує запит (як і слід), тому існуючий $ .ajax (...). Done (функція (відповідь) {...}). Fail (...) тепер перестає працювати, оскільки скоріше викликається fail ніж зроблено ... і я волів би не переписувати їх усіх :(
franck102

2
@TecHunter Велике спасибі за це. Можна зробити ще три незначні вдосконалення. По-перше, якщо одночасно подано кілька запитів до одного ресурсу, вони всі пропуститимуть кеш. Щоб виправити це, можливо, ви захочете встановити кеш для певного «ідентифікатора» на очікування з першим запитом та відкладіть відправлення результатів для наступних запитів до повернення першого. По-друге, ви можете захотіти кешувати результат помилки запиту, щоб усі запити на той самий ресурс отримали однаковий результат.
mjhm

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

12

Я шукав кешування для мого сховища додатків phonegap, і я знайшов відповідь @TecHunter, яка чудова, але зроблена з використанням localCache.

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

Виклик 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");

Сподіваюся, це допомагає іншим!


Привіт, в localStorage.removeItem("Info");, про "info"це це URL-адреса?
vsogrimen

@vsogrimen info- це об'єкт для зберігання даних у localStorage.
immayankmodi

1
продовжуйте отримувати responseJSON is not defined. Будь-яким способом це виправити? (мій тип даних - html)
Микита,

@CreativeMind, видаліть reponseJOSN і використовуйте "var responseData = JSON.stringify (дані);" натомість зробіть те саме з успіхом (дані)
Unicco

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

9

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

Все, що вам потрібно зробити, це після отримання відповіді зберегти його в пам’яті браузера. Потім наступного разу, коли ви знайдете той самий дзвінок, шукайте, чи відповідь вже збережена. Якщо так, поверніть відповідь звідти; якщо не зробити новий дзвінок.

Плагін Smartjax також робить подібні речі; але оскільки вашою вимогою є лише збереження відповіді на дзвінок, ви можете написати свій код у своїй функції успіху jQuery ajax, щоб зберегти відповідь. І перед здійсненням дзвінка просто перевірте, чи відповідь вже збережена.


У мене є відповіді, збережені в IndexedDB, чи є спосіб перевірити IndexedDB? Крім того, якщо я використовую Session Storage, то чи є спосіб перевірити, чи присутня відповідь, чи не використовується jQuery. Я не можу включити жодної бібліотеки, крім jQuery. Дякую,
Абхішек Аггарвал,

7

Якщо я зрозумів ваше запитання, ось рішення:

    $.ajaxSetup({ cache: true});

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

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

Якщо ви хочете протилежне (кеш для конкретних дзвінків), ви можете встановити false на початку та true для конкретних дзвінків.


2
це якраз навпаки
TecHunter

Мені це працює, дякую! Дивно, що кешування за замовчуванням не було ввімкнено на моїй сторінці ..
Тимур Нурлыгаянов

2

Старе питання, але моє рішення трохи інше.

Я писав односторінкову веб-програму, яка постійно робила виклики ajax, ініційовані користувачем, і щоб зробити це ще складнішим, потрібні були бібліотеки, які використовували інші методи, крім jquery (наприклад, dojo, власний 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-бритва
Аліджанскій Каблан

-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.