JavaScript / tarayıcıda bir jquery ajax yanıtını önbelleğe alma


100

Javascript / browser'da bir ajax yanıtının önbelleğe alınmasını etkinleştirmek istiyorum.

Gönderen jquery.ajax docs :

Varsayılan olarak, istekler her zaman yayınlanır, ancak tarayıcı sonuçları önbelleğinden sunabilir. Önbelleğe alınan sonuçların kullanımına izin vermemek için önbelleği yanlış olarak ayarlayın. Varlık son istekten bu yana değiştirilmediyse isteğin başarısızlığı bildirmesine neden olmak için ifModified değerini true olarak ayarlayın.

Ancak, bu adreslerin hiçbiri önbelleğe almayı zorlamaz.

Motivasyon:$.ajax({...}) Bazıları aynı url'yi isteyen başlatma işlevlerime aramalar koymak istiyorum . Bazen bu başlatma işlevlerinden birini çağırmam gerekir, bazen birkaçını arıyorum.

Bu nedenle, belirli url zaten yüklenmişse, sunucuya yapılan istekleri en aza indirmek istiyorum.

Kendi çözümümü alabilirim (biraz zorlukla!), Ancak bunu yapmanın standart bir yolu olup olmadığını bilmek isterim.


Hangi URL'leri önceden yüklediğinizi takip etmenin ve sonuçları bu listeye göre saklamanın zor olacağını düşünmemiştim. Ardından, AJAX çağrısı yapmadan önce URL'lerinizi kontrol edebilirsiniz. Voila - kendi temel önbelleğiniz var.

önbellek denetimini ve sona erme başlığını sunucudaki yanıtınıza ekleyebilirsiniz, bu nedenle sunucunuz yalnızca bu değerlerde yapılandırdığınız zaman aşımından sonra çağrılmalıdır
şimdi78

Çok uzun, ancak ajax isteklerinde neden önbelleğe ihtiyaç duyduğunuzu anlamama yardımcı olabilir misiniz (muhtemelen meslekten
olmayanlar

Yanıtlar:


146

cache:true yalnızca GET ve HEAD isteği ile çalışır.

Söylediğiniz gibi, şu satırlar boyunca bir şeyle kendi çözümünüzü yuvarlayabilirsiniz:

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

Burada keman çalışmak

DÜZENLEME: Bu gönderi popüler hale geldikçe, zaman aşımı önbelleğini yönetmek isteyenler için daha da iyi bir cevap var ve $ .ajaxPrefilter () kullandığım için $ .ajax () içindeki tüm karışıklıklarla uğraşmanıza gerek kalmıyor. . Artık {cache: true}önbelleği doğru bir şekilde ele almak için sadece ayarlama yeterlidir:

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

Ve buradaki keman DİKKATLİ, $ ile çalışmıyor.

Ertelenmiş ile çalışan, çalışan ancak hatalı bir uygulama:

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 Bazı sorunlar, önbellek kimliğimiz json2 lib JSON nesne temsiline bağlıdır.

Önbellek tarafından oluşturulan bazı günlükleri görüntülemek için Konsol görünümünü (F12) veya FireBug kullanın.


İşleve geri arama koymanızın bir nedeni var localCache.setmı? Neden doSomehing(jqXHR)önbelleği ayarladıktan sonra değil ?
cammil

Ben böyle bir şey yapmak zorunda kalmamak ben sadece bu şekilde tercih doSomething(localCache.set(url,jqXHR));ama sadece personnal tercihi
TecHunter

2
$ .Ajax'ı bir söz olarak kullanmayı desteklemek için bunu geliştirmek için herhangi bir öneriniz var mı? BeforeSend öğesinden yanlış döndürmek isteği iptal eder (olması gerektiği gibi), bu nedenle mevcut $ .ajax (...) yapıldı (işlev (yanıt) {...}). Başarısız (...) artık çalışmayı durdurur çünkü başarısızlık yerine çağrılır bitti ... ve hepsini yeniden yazmak istemiyorum :(
franck102

2
@TecHunter Bunun için çok teşekkürler. Üç küçük iyileştirme daha yapılabilir. İlk olarak, aynı kaynağa aynı anda birden fazla istek yapılırsa, hepsi önbelleği kaçıracaktır. Bunu düzeltmek için, belirli bir "kimlik" için önbelleği ilk istekle birlikte beklemeye ayarlamak ve sonraki istekler için sonuçları ilk geri gelene kadar göndermeyi ertelemek isteyebilirsiniz. İkinci olarak, aynı kaynağa yönelik tüm isteklerin aynı sonucu alması için bir isteğin hata sonucunu önbelleğe almak isteyebilirsiniz.
mjhm

2
@TecHunter - Güzel çözüm, bu çözümü önemli bir değişiklikle kullandım. : Önbelleğe nesnenin diğer fonksiyonlarda değişiklik yapılmışsa bunun ayarlanması ve nesne önbelleğe alma sırasında bu yüzden, ben aşağıda gösterildiği gibi bu nesnenin kopyasını iade ediyorum, soruna neden olur localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Bharat Patil

12

Phonegap uygulama depolama alanım için önbelleğe alma arıyordum ve @TecHunter'ın cevabını buldum, bu harika ama kullanılarak yapıldı localCache.

LocalStorage'ın ajax çağrısı tarafından döndürülen verileri önbelleğe almak için başka bir alternatif olduğunu buldum ve öğrendim. Bu yüzden, önbelleğe almak yerine localStoragekullanmak isteyebilecek başkalarına yardımcı olacak bir demo oluşturdum .localStoragelocalCache

Ajax Çağrısı:

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

Verileri localStorage'da depolamak için:

$.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'ı kaldırmak istiyorsanız, istediğiniz yerde aşağıdaki ifadeyi kullanın:

localStorage.removeItem("Info");

Umarım başkalarına yardımcı olur!


Merhaba, localStorage.removeItem("Info");hakkında "info", url mi?
vsogrimen

@vsogrimen info, verileri localStorage'da depolayan nesnedir.
immayankmodi

1
almaya devam edin responseJSON is not defined. Bunu düzeltmenin bir yolu var mı? (benim veri
türüm

@CreativeMind, reponseJOSN'yi kaldırın ve "var responseData = JSON.stringify (data)" kullanın; bunun yerine başarı için aynısını yapın (veriler)
Unicco

Birden çok istekte önbelleğe ihtiyaç duyduğum için localStorage'ı tercih ediyorum.
KeitelDOG

9

Tüm modern tarayıcılar size depolama api'leri sağlar. Verilerinizi kaydetmek için bunları (localStorage veya sessionStorage) kullanabilirsiniz.

Yapmanız gereken tek şey, yanıtı aldıktan sonra tarayıcı depolamasında saklamaktır. Sonra bir dahaki sefere aynı aramayı bulduğunuzda, yanıtın önceden kaydedilmiş olup olmadığını araştırın. Cevabınız evet ise, cevabı oradan geri gönderin; yeni bir arama yapmazsanız.

Smartjax eklentisi de benzer şeyler yapar; ancak gereksiniminiz yalnızca çağrı yanıtını kaydetmek olduğundan, yanıtı kaydetmek için kodunuzu jQuery ajax başarı işlevinin içine yazabilirsiniz. Ve arama yapmadan önce yanıtın önceden kaydedilip kaydedilmediğini kontrol edin.


IndexedDB'ye kaydedilmiş yanıtlarım var, IndexedDB'yi kontrol etmenin bir yolu var mı? Ayrıca, Oturum Depolamasını kullanırsam, jQuery kullanarak yanıtın mevcut olup olmadığını kontrol etmenin bir yolu var mı? JQuery dışında herhangi bir kitaplık ekleyemiyorum. Teşekkürler,
Abhishek Aggarwal

7

Sorunuzu anladıysam, işte çözüm:

    $.ajaxSetup({ cache: true});

ve belirli aramalar için

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

Tersini istiyorsanız (belirli aramalar için önbellek), başlangıçta yanlış ve belirli aramalar için doğru ayarlayabilirsiniz.


2
bu tam tersi
TecHunter

Benim için çalışıyor, teşekkürler! Önbelleğe almanın sayfamda varsayılan olarak etkinleştirilmemiş olması garip ..
Timur Nurlygayanov

2

Eski soru, ama benim çözümüm biraz farklı.

Sürekli olarak kullanıcı tarafından tetiklenen ajax çağrıları yapan tek sayfalık bir web uygulaması yazıyordum ve bunu daha da zorlaştırmak için jquery dışında yöntemler kullanan (dojo, native xhr, vb.) Kütüphanelere ihtiyaç duyuyordum. Ajax çağrısı yapmak için hangi kütüphanelerin kullanıldığına bakılmaksızın, ajax isteklerini tüm büyük tarayıcılarda çalışacak şekilde olabildiğince verimli bir şekilde önbelleğe almak için kendi kütüphanelerimden biri için bir eklenti yazdım .

Çözüm jSQL (benim tarafımdan yazılmıştır - indexeddb ve diğer dom depolama yöntemlerini kullanan, javascript ile yazılmış bir istemci tarafı kalıcı SQL uygulaması) kullanır ve XHRCreep (benim tarafımdan yazılmıştır) adlı başka bir kitaplıkla paketlenmiştir ve yerel XHR nesnesi.

Eğer is sayfanızda eklenti içerir yapmanız gereken uygulamak için burada .

İki seçenek vardır:

jSQL.xhrCache.max_time = 60;

Maksimum yaşı dakika cinsinden ayarlayın. bundan daha eski önbelleğe alınmış yanıtlar yeniden istenir. Varsayılan 1 saattir.

jSQL.xhrCache.logging = true;

True olarak ayarlandığında, hata ayıklama için konsolda sahte XHR çağrıları gösterilecektir.

Herhangi bir sayfadaki önbelleği şu yolla temizleyebilirsiniz:

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

Eklentinizi github'da ve resmi web sitesinde bulamıyorum.Lütfen cevabınızı güncelleyin Eklentinize ihtiyacım var :) github'da takipçisiyim. İyi iş;) @ 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();

bağlantı açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.