ajaxPrefilter 与 ajaxTransport 都是通过 inspectPrefiltersOrTransports 构建器创建的。
prefilters 中的前置过滤器在请求发送之前、设置请求参数的过程中被调用,调用 prefilters 的是函数 inspectPrefiltersOrTransports ,巧妙的是 transports 中的请求分发器在大部分参数设置完成后,也通过函数 inspectPrefiltersOrTransports 取到与请求类型匹配的请求分发器。
通过(右边代码一)我们可以看出来:
前置过滤器 prefilters
在每个请求之前被发送和 $.ajax () 处理它们前处理,设置自定义 Ajax 选项或修改现有选项,简单的说就是一种 hack 的做法,只是说比起事件的那种 hack 写的手法实现更为高明。比如我们要预过滤器(Prefilters)也可以被用来修改已经存在的选项。
例如,下面的代理服务器跨域请求 http://mydomain.net/proxy/:
$.ajaxPrefilter( function( options ) { if ( options.crossDomain ) { options.url = "http://mydomain.net/proxy/" + encodeURIComponent( options.url ); options.crossDomain = false; } });
如果提供可选的 dataTypes 参数,那么预滤器(prefilter)将只会对满足指定 dataTypes 的请求有效。例如, 以下仅适用于 JSON 和 script 请求给定的预过滤器:我们可以看看针对 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);
从源码中可以看到 transport 是一个对象,它提供了两种方法,send
和 abort
,内部使用由 $.ajax()
发出请求。transport 是最高级的方法用来增强 $.ajax()
并且应仅作为当预过滤器(prefilters)和转换器(converters)无法满足你的需求的时候的最后的手段。由于每个请求需要有自己的传输(transport)对象实例,传输不能直接注册。因此,你应该提供一个函数代替返回传输(transport)。
<!doctype html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> <script src="http://code.jquery.com/jquery-latest.js"></script> <title>模拟image的ajaxPrefilter与ajaxTransport处理</title> </head> <body> <button id="test">模拟image的ajaxPrefilter与ajaxTransport处理</button> <ul></ul> <script type="text/javascript"> var $ul = $('ul') function show(data) { $ul.append('<li>' + data + '</li>'); } ////////////////////////////////////////////////////////////////// // options 是请求的选项 // // originalOptions 值作为提供给Ajax方法未经修改的选项,因此,没有ajaxSettings设置中的默认值 // // jqXHR 是请求的jqXHR对象 // ////////////////////////////////////////////////////////////////// $.ajaxPrefilter("image", function(options, originalOptions, jqXHR) { //通过预处理器转化类型 if (options.test) { options.type = 'GET' } //增加前缀 options.url = "http://img1.sycdn.imooc.com//" + options.url }); /////////////////////// // 请求分发器 transports // /////////////////////// $.ajaxTransport("image", function(s) { if (s.type === "GET" && s.async) { var image; return { send: function(_, callback) { image = new Image(); function done(status) { if (image) { var statusText = (status == 200) ? "success" : "error", tmp = image; image = image.onreadystatechange = image.onerror = image.onload = null; callback(status, statusText, { image: tmp }); } } image.onreadystatechange = image.onload = function() { done(200); }; image.onerror = function() { done(404); }; show(s.url) image.src = s.url; }, abort: function() { if (image) { image = image.onreadystatechange = image.onerror = image.onload = null; } } }; } }); $("#test").click(function(){ //执行一个异步的HTTP(Ajax)的请求。 var ajax = $.ajax({ test : true, //测试 url : '547d5a45000156f406000338-590-330.jpg', dataType : 'image', type : 'POST', data: { foo: ["bar1", "bar2"] }, //这个对象用于设置Ajax相关回调函数的上下文 context: document.body, //请求发送前的回调函数,用来修改请求发送前jqXHR beforeSend: function(xhr) { xhr.overrideMimeType("text/plain; charset=x-user-defined"); show('局部事件beforeSend') }, //请求完成后回调函数 (请求success 和 error之后均调用) complete: function() { show('局部事件complete') }, error: function() { show('局部事件error请求失败时调用此函数') }, success: function() { show('局部事件success') } }) ajax.done(function() { show('done') }).fail(function() { show('fail') }).always(function() { show('always') }) }) </script> </body> </html>