在javascript/browser中缓存jquery ajax响应

cam*_*mil 92 javascript browser ajax jquery caching

我想在javascript/browser中启用ajax响应的缓存.

来自jquery.ajax文档:

默认情况下,始终发出请求,但浏览器可能会从其缓存中提供结果.要禁止使用缓存的结果,请将cache设置为false.如果自上次请求后资产未被修改,则导致请求报告失败,请将ifModified设置为true.

但是,这些都不会强制缓存.

动机: 我想$.ajax({...})在我的初始化函数中调用,其中一些函数请求相同的url.有时我需要调用其中一个初始化函数,有时我会调用几个函数.

因此,如果已经加载了特定的URL,我想最小化对服务器的请求.

我可以推出自己的解决方案(有些困难!),但我想知道是否有一种标准的方法可以做到这一点.

Tec*_*ter 140

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);
}
Run Code Online (Sandbox Code Playgroud)

在这里工作小提琴

编辑:随着这篇文章变得流行,对于那些想要管理超时缓存的人来说,这是一个更好的答案,你也不必为$ .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);
}
Run Code Online (Sandbox Code Playgroud)

这里的小提琴 小心翼翼,不与$ .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
            });
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

这里 有一些问题,我们的缓存ID取决于json2 lib JSON对象表示.

使用控制台视图(F12)或FireBug查看缓存生成的某些日志.

  • 是否有任何改进建议以支持使用$ .ajax作为承诺?从beforeSend返回false取消请求(因为它应该),因此现有的$ .ajax(...).done(function(response){...}).fail(...)现在停止工作,因为失败被调用而不是比做完...我宁愿不重写所有:( (2认同)
  • @TecHunter非常感谢.可以进行三项略微改进.首先,如果同时对同一资源发出多个请求,则它们都会错过缓存.要解决此问题,您可能希望将第一个请求的特定"id"的缓存设置为挂起,并推迟发送后续请求的结果,直到第一个请求返回.其次,您可能希望缓存请求的错误结果,以便对同一资源的所有请求获得相同的结果. (2认同)
  • @TecHunter - 很好的解决方案,我使用此解决方案进行了一项重要更改.如果在其他函数中修改了缓存对象,则会导致问题,因此在设置和获取缓存对象时,我将返回该对象的副本,如下所示:`localCache.data [url] = {_:new Date() .getTime(),data:_. cloneDeep(cachedData,true)}; _.cloneDeep(localCache.data [url] .data,true)` (2认同)

imm*_*odi 11

我正在为我的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!");
    }
});
Run Code Online (Sandbox Code Playgroud)

要将数据存储到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
    };
}
});
Run Code Online (Sandbox Code Playgroud)

如果要删除localStorage,请在任何位置使用以下语句:

localStorage.removeItem("Info");
Run Code Online (Sandbox Code Playgroud)

希望它能帮到别人!


Pau*_*han 8

所有现代浏览器都为您提供存储api.您可以使用它们(localStorage或sessionStorage)来保存数据.

您所要做的就是在收到响应后将其存储到浏览器存储中.然后,下次找到相同的呼叫时,搜索是否已保存响应.如果是,请从那里返回答复; 如果没有打个电话.

Smartjax插件也做类似的事情; 但是由于您的要求只是保存调用响应,您可以在jQuery ajax成功函数中编写代码以保存响应.在拨打电话之前,请检查响应是否已保存.


小智 6

如果我理解你的问题,这是解决方案:

    $.ajaxSetup({ cache: true});
Run Code Online (Sandbox Code Playgroud)

和特定的电话

 $.ajax({
        url: ...,
        type: "GET",
        cache: false,           
        ...
    });
Run Code Online (Sandbox Code Playgroud)

如果您想要相反(特定调用的缓存),您可以在开头设置false,为特定调用设置true.

  • 恰恰相反 (2认同)