define(function (require, exports, module) {var $ = require("jquery");//根据关键的几个参数统一创建ajax对象function create(_url, _method, _data, _async, _dataType) {//添加随机数if (_url.indexOf("?") > -1) {_url = _url + "&rnd=" + Math.random();} else {_url = _url + "?rnd=" + Math.random();}//为请求添加ajax标识,方便后台区分ajax和非ajax请求_url += "&_ajax=1";//返回jquery创建的ajax对象,以便外部拿到这个对象以后可以通过//.done .fail .always来添加回调//这么做是为了保留jquery ajax中好用的部分return $.ajax({url: _url,dataType: _dataType,async: _async,method: _method,data: _data});}//ajax就是本组件全局唯一的实例,它的实例方法通过后面的循环代码添加//methods对象配置ajax各个实例方法的参数://name: 方法名称//method: http请求方法,get or post//async: 发送请求时是否异步//dataType: 返回的数据类型,html or jsonvar ajax = {},methods = [{name: "html",method: "get",async: true,dataType: "html"},{name: "get",method: "get",async: true,dataType: "json"},{name: "post",method: "post",async: true,dataType: "json"},{name: "syncGet",method: "get",async: false,dataType: "json"},{name: "syncPost",method: "post",async: false,dataType: "json"}];//由于二次封装需要对外提供的每个实例方法创建ajax的逻辑是相同的//所以通过这种方式统一定义各个实例方法//关键代码为下面代码中的那个立即调用的函数//它返回了一个新的闭包函数作为实例方法for (var i = 0, l = methods.length; i < l; i++) {ajax[methods[i].name] = (function (i) {return function () {/** * 每个实例方法接收三个参数 * 第一个表示要请求的地址 * 第二个表示要提交到后台的数据,是一个object对象,如{param1: "value1"} * 第三个表示后台返回的数据类型,最最常用的就是html or json,绝大部分情况下这个参数不用传,会使用methods里面定义的dataType */var _url = arguments[0],_data = arguments[1],_dataType = arguments[2] || methods[i].dataType;return create(_url, methods[i].method, _data, methods[i].async, _dataType);}})(i);}return ajax;});这个Ajax组件主要做的事情是: define(function (require, exports, module) {var Ajax = require("mod/ajax");//以GET方式请求html内容Ajax.html("html/demo", {param1: "value1",param2: "value2"}).done(function(response){//请求成功的回调}).fail(function(){//请求失败的回调}).always(function(){//请求完成的回调});//以GET方式请求json数据Ajax.get("api/demo", {param1: "value1",param2: "value2"}).done(function(response){//请求成功的回调}).fail(function(){//请求失败的回调}).always(function(){//请求完成的回调});//以POST方式请求json数据Ajax.post("api/demo", {param1: "value1",param2: "value2"}).done(function(response){//请求成功的回调}).fail(function(){//请求失败的回调}).always(function(){//请求完成的回调});//以GET方式发送同步请求,获取json数据Ajax.syncGet("api/demo", {param1: "value1",param2: "value2"}).done(function(response){//请求成功的回调}).fail(function(){//请求失败的回调}).always(function(){//请求完成的回调});//以POST方式发送同步请求,获取json数据Ajax.syncPost("api/demo", {param1: "value1",param2: "value2"}).done(function(response){//请求成功的回调}).fail(function(){//请求失败的回调}).always(function(){//请求完成的回调});});由于这个组件的每个实例方法返回的对象就是$.ajax创建的对象,所以我们完全可以照常使用.done .fail .always来添加回调,就跟直接用$.ajax没有任何区别。为什么API要设计成html, get, post, syncGet, syncPost这几个方法,而且连dataType基本都是固定的?define(function (require, exports, module) {var $ = require("jquery");var Ajax = require("mod/ajax");//缓存列表var cache = {};/** * 生成缓存索引: * 由于索引是根据url和data生成的(data是一个对象,存放Ajax要提交到后台的数据) * 所以要想同一个url,同样的data能够有效地使用缓存, * 切勿在url和data中包含每次可变的参数值,如随机数等 * 比如有一个请求: * url: aaa/bbb/cccc?r=0.312738 * data: {name: "json"} * 其中url后面的r是一个随机数,每次外部发起这个请求时,r的值都会变化 * 由于r每次都不同,最终会导致缓存索引不相同,结果缓存就无法命中 * 注:随机数可放置在原始的Ajax组件内 * * 还有:如果是同一个接口,最好在同一个页面内,统一url的路径类型,要么都是相对路径,要么都是绝对路径 * 否则也会导致缓存无法有效管理 */function generateCacheKey(url, data) {return url + $.param(data);}return function (opts) {opts = opts || {};var cacheInterval = opts.cacheInterval || (1000 * 60 * 60);//缓存有效时间,默认60分钟var proxy = {};for (var i in Ajax) {if (Object.prototype.hasOwnProperty.call(Ajax, i)) {//在proxy对象上定义Ajax组件每一个实例方法的代理//注意这个立即调用的函数表达式//它返回了一个闭包函数就是最终的代理方法proxy[i] = (function (i) {return function () {var _url = arguments[0],_data = arguments[1],cacheKey = generateCacheKey(_url, _data),cacheItem = cache[cacheKey],isCacheValid = false;if (cacheItem) {var curTime = +new Date();if (curTime - cacheItem.cacheStartTime <= cacheInterval) {//如果请求时间跟缓存开始时间的间隔在缓存有效时间范围内,就表示缓存是有效的isCacheValid = true;} else {//否则就把缓存清掉delete cache[cacheKey];}}if (isCacheValid) {//模拟一个异步任务来返回已经缓存的数据//通过$defer延迟对象,可以保证这个模拟任务返回的对象跟原始Ajax组件调用返回的对象有相同的API//这是代理的关键:代理对象与被代理的对象应该具有相同API//只有这样当我们取消代理的时候,不会对那些用了代理的组件进行修改var $defer = $.Deferred();setTimeout(function () {$defer.resolve(cacheItem.res);}, 10);return $.when($defer);}//缓存失效或者没有缓存的时候调用原始的Ajax组件的同名方法去后台请求数据return Ajax[i].apply(Ajax, arguments).done(function (res) {//在请求成功之后将结果缓存,并记录当前时间作为缓存的开始时间cache[cacheKey] = {res: res,cacheStartTime: +new Date()}});}})(i);}}return proxy;};});为了说明缓存代理的使用效果,我做了一个演示效果:
其中的ajax.js就是第一部分的实现,ajaxCache.js就是第二部分的实现,演示页面对应代码中的html/demo.html,相关js是js/app/demo.js:
define(function (require, exports, module) {var AjaxCache = require("mod/ajaxCache");//创建代理对象var Ajax = new AjaxCache({cacheInterval: 10 * 1000});var count = 5;console.log("时间点:第" + 0 + "s,定时器开始!");var t = setInterval(function(){if(count == 0) {console.log("时间点:第" + (5 - count + 1) * 4 + "s,定时器结束!");return clearInterval(t);} else{console.log("时间点:第" + (5 - count + 1) * 4 + "s:");}Ajax.get("../api/data.json", {name: "felix"}).done(function(res){if(res.code == 200) {console.log(5 - count + ". data is : " + JSON.stringify(res.data));}});count --;},4000);});在这个代码中,我创建了一个代理对象,能将ajax请求缓存10s,用一个定时器一共调用代理对象五次get方法来发送同一个请求,最终打印效果如下:
从结果来看,整个代码执行了24s,代理发送请求的时间点分别是第4s,第8s,第12s,第16s和第20s。由于代理的缓存有效时间是10s,且第4s是第一次发送请求,所以此时肯定会发送真实的ajax请求;当第8s和第12s的代理发送同一请求时,由于距离缓存的时间只过去了4s和8s,所以缓存还是有效的,这两个时间点都没有发送实际的ajax请求;但是当第16s的请求发送时,距离第一次请求的缓存时间已经过去12s,缓存已经失效,所以代理又发送了一次真实的ajax请求,然后缓存被刷新;第20s的请求还是在最新的缓存有效时间内,所以也没有发送实际的ajax请求。最后在network中可以看到代理发送了5次请求,但是只请求了2次服务,如果把缓存有效时间延长,就能再减少请求后台的次数,这也是缓存代理对前端性能提升的关键。
希望这个演示效果能够让你更加清楚地了解缓存代理的作用。
4. 本文总结
本文第1部分总结的实现在我自己的工作中应用很多,至少没碰到什么问题,不过也有可能是我没遇到,毕竟那个组件实现还是有不少约束的。第2部分的实现我也是刚刚应用到工作中去,正好有一个功能我考虑到有缓存的必要性,于是就写了一个较为简单的实现,虽然简单,但是已经能解决我的问题了,实际工作本来就是这样,有些东西没必要事无巨细的在事前就设计地很完美,先解决问题,然后在遇到新问题的时候再回来重构,有时也是一种更好的工作方法。下一篇博客介绍另外一个用到缓存代理的组件的实现思路,跟省市级联类似的功能,不过我想的是写成通用性更强的,能够与html结构和css尽可能分离的组件,请您继续关注。
以上这篇对jquery的ajax进行二次封装以及ajax缓存代理组件:AjaxCache详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。