การแคชการตอบสนอง jquery ajax ใน javascript / browser


100

ฉันต้องการเปิดใช้งานการแคชการตอบสนองของ ajax ใน javascript / browser

จากเอกสาร jquery.ajax :

โดยค่าเริ่มต้นจะมีการส่งคำขอเสมอ แต่เบราว์เซอร์อาจแสดงผลลัพธ์จากแคช หากต้องการไม่อนุญาตให้ใช้ผลลัพธ์ที่แคชให้ตั้งค่าแคชเป็นเท็จ หากต้องการทำให้คำขอรายงานล้มเหลวหากเนื้อหาไม่ได้รับการแก้ไขตั้งแต่คำขอล่าสุดให้ตั้งค่า ifModified เป็น true

อย่างไรก็ตามที่อยู่เหล่านี้ไม่ได้บังคับให้แคช

แรงจูงใจ: ฉันต้องการวาง$.ajax({...})สายในฟังก์ชันการเริ่มต้นของฉันซึ่งบางส่วนขอ url เดียวกัน บางครั้งฉันต้องเรียกใช้ฟังก์ชัน initialisation เหล่านี้บางครั้งฉันเรียกหลาย ๆ

ดังนั้นฉันต้องการลดคำขอไปยังเซิร์ฟเวอร์หากมีการโหลด url นั้นแล้ว

ฉันสามารถม้วนโซลูชันของตัวเองได้ (ด้วยความยากลำบาก!) แต่ฉันอยากรู้ว่ามีวิธีมาตรฐานในการทำเช่นนี้หรือไม่


ฉันคงไม่คิดว่าจะยากที่จะติดตามว่าคุณได้โหลด URL ใดไปแล้วและเก็บผลลัพธ์ไว้ในรายการนั้น จากนั้นคุณสามารถตรวจสอบ URL ของคุณก่อนที่จะโทรหา AJAX Voila - คุณมีแคชพื้นฐานของคุณเอง

คุณสามารถเพิ่มส่วนควบคุมแคชและหมดอายุในการตอบกลับของคุณบนเซิร์ฟเวอร์ดังนั้นเซิร์ฟเวอร์ของคุณควรถูกเรียกใช้หลังจากหมดเวลาที่คุณกำหนดค่าไว้ในค่านั้นเท่านั้น
ณ ที่นี้ 78

มันยาวมาก แต่คุณช่วยให้ฉันเข้าใจได้ไหมว่าทำไมคุณถึงต้องการแคชในคำขอ ajax (อาจเป็นในแง่ของคนธรรมดา)
Asish

คำตอบ:


147

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 บางปัญหา ID แคชของเราขึ้นอยู่กับการแสดงวัตถุ json2 lib JSON

ใช้มุมมองคอนโซล (F12) หรือ FireBug เพื่อดูบันทึกบางรายการที่สร้างโดยแคช


มีเหตุผลที่คุณเรียกกลับในlocalCache.setฟังก์ชันนี้หรือไม่ ทำไมไม่doSomehing(jqXHR)หลังจากตั้งค่าแคชแล้วล่ะ?
cammil

ฉันแค่ชอบวิธีนี้มากฉันจึงไม่ต้องทำอะไรที่ชอบdoSomething(localCache.set(url,jqXHR));แต่มันเป็นแค่ความชอบ
ส่วนตัวเท่านั้น

2
ข้อเสนอแนะในการปรับปรุงสิ่งนี้เพื่อรองรับการใช้ $ .ajax เป็นสัญญาหรือไม่? การส่งคืนเท็จจาก beforeSend จะยกเลิกคำขอ (ตามที่ควร) ดังนั้น $ .ajax (... ) ที่มีอยู่ done (function (response) {... }). fail (... ) ตอนนี้หยุดทำงานเนื่องจากมีการเรียกใช้ fail กว่าจะเสร็จ ... และฉันไม่อยากจะเขียน
ซ้ำ

2
@TecHunter ขอบคุณมากสำหรับสิ่งนี้ สามารถปรับปรุงเพิ่มเติมเล็กน้อยได้สามประการ ขั้นแรกหากมีการร้องขอหลายรายการไปยังทรัพยากรเดียวกันในเวลาเดียวกันพวกเขาทั้งหมดจะพลาดแคช ในการแก้ไขปัญหานี้คุณอาจต้องการตั้งค่าแคชสำหรับ "id" บางรายการเพื่อรอดำเนินการกับคำขอแรกและเลื่อนการส่งผลลัพธ์สำหรับคำขอที่ตามมาจนกว่าคำขอแรกจะกลับ ประการที่สองคุณอาจต้องการแคชผลลัพธ์ข้อผิดพลาดของคำขอเพื่อให้คำขอทั้งหมดสำหรับทรัพยากรเดียวกันได้รับผลลัพธ์เดียวกัน
mjhm

2
@TecHunter - ทางออกที่ดีฉันใช้วิธีนี้กับการเปลี่ยนแปลงที่สำคัญอย่างหนึ่ง หากมีการแก้ไขวัตถุที่แคชในฟังก์ชันอื่น ๆ มันจะทำให้เกิดปัญหาดังนั้นในขณะที่ตั้งค่าและรับวัตถุที่แคชฉันจะส่งคืนสำเนาของวัตถุนั้นซึ่งตามที่แสดงด้านล่าง: localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Bharat Patil

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)
Nikita 웃

@CreativeMind ลบ reponseJOSN และใช้ "var responseData = JSON.stringify (data);" ทำเช่นเดียวกันกับความสำเร็จแทน (ข้อมูล)
Unicco

ฉันชอบ localStorage มากกว่าเพราะฉันต้องการแคชในหลาย ๆ คำขอ
KeitelDOG

9

เบราว์เซอร์ที่ทันสมัยทั้งหมดให้พื้นที่จัดเก็บข้อมูลแก่คุณ คุณสามารถใช้ (localStorage หรือ sessionStorage) เพื่อบันทึกข้อมูลของคุณ

สิ่งที่คุณต้องทำคือหลังจากได้รับคำตอบแล้วให้เก็บไว้ในที่เก็บข้อมูลของเบราว์เซอร์ จากนั้นครั้งต่อไปที่คุณพบการโทรเดียวกันให้ค้นหาว่าการตอบกลับถูกบันทึกไว้แล้วหรือไม่ ถ้าใช่ให้ตอบกลับจากที่นั่น ถ้าไม่โทรใหม่

ปลั๊กอินSmartjaxยังทำสิ่งที่คล้ายกัน แต่เนื่องจากความต้องการของคุณเป็นเพียงการบันทึกการตอบกลับคุณสามารถเขียนโค้ดของคุณภายในฟังก์ชัน jQuery ajax success เพื่อบันทึกการตอบกลับ และก่อนโทรออกให้ตรวจสอบว่าการตอบกลับได้รับการบันทึกแล้ว


ฉันได้บันทึกคำตอบไว้ใน IndexedDB มีวิธีตรวจสอบ IndexedDB หรือไม่ นอกจากนี้หากฉันใช้ Session Storage จะมีวิธีตรวจสอบว่ามีการตอบสนองหรือไม่โดยใช้ jQuery ฉันไม่สามารถรวมไลบรารีใด ๆ นอกจาก jQuery ขอบคุณ
Abhishek Aggarwal

7

หากฉันเข้าใจคำถามของคุณนี่คือวิธีแก้ปัญหา:

    $.ajaxSetup({ cache: true});

และสำหรับการโทรเฉพาะ

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

หากคุณต้องการตรงข้าม (แคชสำหรับการโทรเฉพาะ) คุณสามารถตั้งค่าเท็จที่จุดเริ่มต้นและเป็นจริงสำหรับการโทรเฉพาะ


2
นั่นตรงกันข้าม
TecHunter

มันได้ผลสำหรับฉันขอบคุณ! เป็นเรื่องแปลกที่ไม่ได้เปิดใช้งานการแคชเป็นค่าเริ่มต้นในหน้าของฉัน ..
Timur Nurlygayanov

จะถูกแคชเป็นเวลาเท่าใด
Pardeep Jain

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;

เมื่อตั้งค่าเป็นจริงการเรียก XHR จำลองจะแสดงในคอนโซลสำหรับการดีบัก

คุณสามารถล้างแคชในหน้าใดก็ได้ผ่าน

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

ฉันไม่พบปลั๊กอินของคุณใน github และเว็บไซต์อย่างเป็นทางการโปรดอัปเดตคำตอบของคุณฉันต้องการปลั๊กอินของคุณ :) ฉันติดตามคุณใน github Good job;) @ 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();

ป้อนคำอธิบายลิงก์ที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.