Caching respons ajax jquery di javascript / browser


98

Saya ingin mengaktifkan cache dari respons ajax di javascript / browser.

Dari dokumen jquery.ajax :

Secara default, permintaan selalu dikeluarkan, tetapi browser mungkin menyajikan hasil dari cache-nya. Untuk melarang penggunaan hasil cache, setel cache ke false. Untuk menyebabkan permintaan melaporkan kegagalan jika aset belum diubah sejak permintaan terakhir, setel ifModified ke true.

Namun, tidak satu pun dari alamat ini yang memaksa penyimpanan ke cache.

Motivasi: Saya ingin melakukan $.ajax({...})panggilan dalam fungsi inisialisasi saya, beberapa di antaranya meminta url yang sama. Terkadang saya perlu memanggil salah satu fungsi inisialisasi ini, terkadang saya memanggil beberapa.

Jadi, saya ingin meminimalkan permintaan ke server jika url tertentu telah dimuat.

Saya dapat menggulirkan solusi saya sendiri (dengan sedikit kesulitan!), Tetapi saya ingin tahu apakah ada cara standar untuk melakukan ini.


Saya tidak akan merasa kesulitan untuk melacak URL mana yang telah Anda muat dan menyimpan hasilnya pada daftar itu. Kemudian, Anda dapat memeriksa URL Anda sebelum melakukan panggilan AJAX. Voila - Anda memiliki cache dasar Anda sendiri.

Anda dapat menambahkan cache-control dan header kedaluwarsa ke respons Anda di server, sehingga server Anda hanya boleh dipanggil setelah waktu tunggu yang Anda konfigurasikan dalam nilai itu
sekarang78

Jawaban:


145

cache:true hanya bekerja dengan permintaan GET dan HEAD.

Anda dapat menggulung solusi Anda sendiri seperti yang Anda katakan dengan sesuatu di sepanjang baris ini:

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

Bekerja biola di sini

EDIT: saat posting ini menjadi populer, berikut adalah jawaban yang lebih baik bagi mereka yang ingin mengelola cache batas waktu dan Anda juga tidak perlu repot dengan semua kekacauan di $ .ajax () karena saya menggunakan $ .ajaxPrefilter () . Sekarang pengaturan saja {cache: true}sudah cukup untuk menangani cache dengan benar:

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

Dan biola di sini HATI-HATI, tidak bekerja dengan $. Ditunda

Berikut adalah implementasi yang berfungsi tetapi cacat bekerja dengan 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
            });
        });
    });
});

Mainkan DI SINI Beberapa masalah, ID cache kami bergantung pada representasi objek JSON json2 lib.

Gunakan tampilan Konsol (F12) atau FireBug untuk melihat beberapa log yang dihasilkan oleh cache.


apakah ada alasan Anda melakukan panggilan balik pada localCache.setfungsi tersebut? Mengapa tidak hanya doSomehing(jqXHR)setelah mengatur cache?
cammil

Saya lebih suka dengan cara ini jadi saya tidak perlu melakukan sesuatu seperti doSomething(localCache.set(url,jqXHR));itu tetapi itu hanya preferensi pribadi
TecHunter

2
Adakah saran untuk meningkatkan ini untuk mendukung penggunaan $ .ajax sebagai janji? Mengembalikan false dari beforeSend membatalkan permintaan (sebagaimana mestinya) sehingga $ .ajax (...). Done (function (response) {...}). Fail (...) sekarang berhenti berfungsi karena fail dipanggil sebagai gantinya daripada selesai ... dan saya lebih suka tidak menulis ulang semuanya :(
franck102

2
@TecHunter Terima kasih banyak untuk ini. Tiga perbaikan kecil lagi bisa dilakukan. Pertama, jika beberapa permintaan dibuat ke sumber daya yang sama pada saat yang sama, semuanya akan kehilangan cache. Untuk memperbaikinya, Anda mungkin ingin menyetel cache untuk "id" tertentu menjadi menunggu permintaan pertama dan menunda pengiriman hasil untuk permintaan berikutnya sampai yang pertama kembali. Kedua, Anda mungkin ingin men-cache hasil error dari sebuah permintaan sehingga semua permintaan untuk resource yang sama mendapatkan hasil yang sama.
mjhm

2
@TecHunter - Solusi bagus, saya menggunakan solusi ini dengan satu perubahan penting. Jika objek yang di-cache sedang dimodifikasi di fungsi lain, itu akan menyebabkan masalah, jadi saat menyetel dan mendapatkan objek yang di-cache, saya mengembalikan salinan objek itu yang seperti yang ditunjukkan di bawah ini: localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Bharat Patil

12

Saya sedang mencari caching untuk penyimpanan aplikasi phonegap saya dan saya menemukan jawaban dari @TecHunter yang sangat bagus tetapi sudah selesai digunakan localCache.

Saya menemukan dan mengetahui bahwa localStorage adalah alternatif lain untuk menyimpan data yang dikembalikan oleh panggilan ajax. Jadi, saya membuat satu demo localStorageyang akan membantu orang lain yang mungkin ingin menggunakan localStoragedaripada localCacheuntuk caching.

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

Untuk menyimpan data ke 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
    };
}
});

Jika Anda ingin menghapus localStorage, gunakan pernyataan berikut dimanapun Anda inginkan:

localStorage.removeItem("Info");

Semoga bisa membantu orang lain!


Hai, di localStorage.removeItem("Info");, "info"apakah itu urlnya?
vsogrimen

@vsogrimen infoadalah objek untuk menyimpan data di localStorage.
immayankmodi

1
terus dapatkan responseJSON is not defined. Adakah cara untuk memperbaikinya? (tipe data saya adalah html)
Nikita 웃

@CreativeMind, hapus reponseJOSN, dan gunakan "var responseData = JSON.stringify (data);" alih-alih, lakukan hal yang sama dengan kesuksesan (data)
Unicco

Saya lebih suka localStorage karena saya membutuhkan cache di beberapa permintaan.
KeitelDOG

9

Semua browser modern memberi Anda apis penyimpanan. Anda dapat menggunakannya (localStorage atau sessionStorage) untuk menyimpan data Anda.

Yang harus Anda lakukan adalah setelah menerima respon, simpan ke penyimpanan browser. Kemudian lain kali Anda menemukan panggilan yang sama, cari jika respons sudah disimpan. Jika ya, kembalikan tanggapan dari sana; jika tidak membuat panggilan baru.

Plugin Smartjax juga melakukan hal serupa; tetapi karena kebutuhan Anda hanya menyimpan respons panggilan, Anda dapat menulis kode Anda di dalam fungsi sukses ajax jQuery untuk menyimpan respons. Dan sebelum melakukan panggilan periksa saja apakah responsnya sudah disimpan.


Saya memiliki tanggapan yang disimpan di IndexedDB, apakah ada cara untuk memeriksa IndexedDB? Juga, jika saya menggunakan Session Storage, apakah ada cara untuk memeriksa apakah ada respons atau tidak menggunakan jQuery. Saya tidak dapat menyertakan pustaka apa pun selain jQuery. Terima kasih,
Abhishek Aggarwal

7

Jika saya mengerti pertanyaan Anda, inilah solusinya:

    $.ajaxSetup({ cache: true});

dan untuk panggilan tertentu

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

Jika Anda ingin sebaliknya (cache untuk panggilan tertentu) Anda dapat mengatur false di awal dan benar untuk panggilan tertentu.


2
justru sebaliknya
TecHunter

Ini berhasil untuk saya, terima kasih! Aneh bahwa cache tidak diaktifkan secara default di halaman saya ..
Timur Nurlygayanov

2

Pertanyaan lama, tetapi solusi saya sedikit berbeda.

Saya sedang menulis aplikasi web satu halaman yang terus-menerus membuat panggilan ajax yang dipicu oleh pengguna, dan untuk membuatnya lebih sulit lagi diperlukan pustaka yang menggunakan metode selain jquery (seperti dojo, native xhr, dll). Saya menulis sebuah plugin untuk salah satu perpustakaan saya sendiri untuk menyimpan permintaan ajax seefisien mungkin dengan cara yang akan bekerja di semua browser utama, terlepas dari perpustakaan mana yang digunakan untuk melakukan panggilan ajax.

Solusinya menggunakan jSQL (ditulis oleh saya - implementasi SQL persisten sisi klien yang ditulis dalam javascript yang menggunakan indexeddb dan metode penyimpanan dom lainnya), dan dibundel dengan pustaka lain yang disebut XHRCreep (ditulis oleh saya) yang merupakan penulisan ulang lengkap dari objek XHR asli.

Untuk menerapkan semua yang perlu Anda lakukan adalah memasukkan plugin di halaman Anda, yang ada di sini .

Ada dua pilihan:

jSQL.xhrCache.max_time = 60;

Tetapkan usia maksimal dalam hitungan menit. setiap tanggapan yang disimpan dalam cache yang lebih lama dari ini akan diminta ulang. Default-nya adalah 1 jam.

jSQL.xhrCache.logging = true;

Jika disetel ke true, panggilan XHR tiruan akan ditampilkan di konsol untuk proses debug.

Anda dapat mengosongkan cache pada halaman mana pun melalui

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

Saya tidak dapat menemukan plugin Anda di github dan situs resmi. Harap perbarui jawaban Anda Saya butuh plugin Anda :) Saya pengikut Anda di github. Kerja bagus;) @ 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();

masukkan deskripsi tautan di sini

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.