jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器
jQuery1.5以后,AJAX模块提供了三个新的方法用于管理、扩展AJAX请求,分别是:
2.请求分发器 jQuery. ajaxTransport,
3.类型转换器 ajaxConvert
源码结构:
jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 * @type {[type]} */ ajaxTransport: addToPrefiltersOrTransports(transports), ........................... });
可见这2个方法是通过私有方法addToPrefiltersOrTransports通过curry手段构造的,分别是保持了prefilters与transports的引用
来个简单的模拟这个结构
var prefilters = 2; var addToPrefiltersOrTransports = function(prefilters) { return function(b) { return prefilters + b; } } var ajaxPrefilter = addToPrefiltersOrTransports(prefilters) ajaxPrefilter(1) //3
可见ajaxPrefilter就维持了addToPrefiltersOrTransports返回函数的引用了,这种就是闭包的手法了,这也是JS的开发人员都需要掌握的
好处就是合并多个参数,当然因为维持引用代价就是一点点性能消耗
当然jQuery不是传递的简单类型处理,还可以传递的一个引用类型的回调函数,所以针对ajaxPrefilter方法放闭包构件就需要做一些处理了
填充prefilters处理器
var prefilters = {}; var addToPrefiltersOrTransports = function(structure) { return function(func) { structure['*'] = func; } } var ajaxPrefilter = addToPrefiltersOrTransports(prefilters) ajaxPrefilter(function(options){ return { send:function(){ }, callback:function(){ } } })
其实说白了就是把对应的方法制作能函数的形式填充到prefilters或者transports对应的处理包装对象中
要用的时候直接执行,每个函数都保持着各自的引用
这种写法的好处自然是灵活,易维护,减少代码量
还有我们经常的使用的,jQuery的代码很简练,比如合并多个方法的创建等等
jQuery.each([ "tabIndex", "readOnly", "maxLength", "contentEditable" ], function() { jQuery.propFix[this.toLowerCase()] = this; });
所以此时的prefilters中的结构就是
prefilters = { '*': function() { return { send: function() { }, callback: function() { } } } }
回归重点,那么引入ajaxPrefilter与ajaxTransport的作用是干嘛?
前置过滤器和请求分发器在执行时,分别遍历内部变量prefilters和transports,这两个变量在jQuery加载完毕后立即初始化,从过闭包的方法填充这个2个对象
ajaxPrefilter与ajaxTransport都是通过inspectPrefiltersOrTransports构建器
prefilters中的前置过滤器在请求发送之前、设置请求参数的过程中被调用,调用prefilters的是函数inspectPrefiltersOrTransports;
巧妙的是,transports中的请求分发器在大部分参数设置完成后,也通过函数inspectPrefiltersOrTransports取到与请求类型匹配的请求分发器:
function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) { var inspected = {}, seekingTransport = (structure === transports); function inspect(dataType) { var selected; inspected[dataType] = true; jQuery.each(structure[dataType] || [], function(_, prefilterOrFactory) { var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR); if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) { options.dataTypes.unshift(dataTypeOrTransport); inspect(dataTypeOrTransport); return false; } else if (seekingTransport) { return !(selected = dataTypeOrTransport); } }); return selected; } return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*"); }
遍历structure[dataType]数组,并执行回调
prefilterOrFactory为函数数组元素
执行该函数如果返回的结果dataTypeOrTransport是字符串且时prefilters且没有被inspected过
就给options.dataTypes数组头部添加该字符串
继续递归dataTypeOrTransport(当我们使用json/jsonp的时候会返回“script”,于是会执行“script”相关的回调)
如果是transport就返回dataTypeOrTransport的假结果
前置过滤器 prefilters
简单的说就是一种hack的做法,只是说比起事件的那种hack写的手法实现更为高明
我们可以看看针对prefilters的方法其实就是dataType为 script,json,jsonp的处理
当我们动态加载脚本文件比如
$.ajax({ type : "GET", url : "test.js", dataType : "script" });
所以在inspectPrefiltersOrTransports方法中prefilters[script]能找到对应的处理方法,所以就会执行
例如script的hack,要强制加上处理缓存的特殊情况和crossDomain
因为设置script的前置过滤器,script并不一定意思着跨域
跨域未被禁用,强制类型为GET,不触发全局时间
jQuery.ajaxPrefilter("script", function(s) { if (s.cache === undefined) { s.cache = false; } if (s.crossDomain) { s.type = "GET"; } });
所以prefilters就是在特定的环境针对特定的情况做一些必要的兼容的处理
请求分发器 transports
请求分发器顾名思义发送请求,那么底层的ajax发送请求是通过send方法
xhr.send();
但是jQuery对send方法做了拆分,把对应的处理放到了transports中了
那么transports对象也是类似前置处理器通过jQuery.ajaxTransport构建
例如script,send,abort方法
返回出transports方法
transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);