6-6 ajaxPrefilter与ajaxTransport
本节编程练习不计算学习进度,请电脑登录imooc.com操作

ajaxPrefilter与ajaxTransport

ajaxPrefilter 与 ajaxTransport 都是通过 inspectPrefiltersOrTransports 构建器创建的。

prefilters 中的前置过滤器在请求发送之前、设置请求参数的过程中被调用,调用 prefilters 的是函数 inspectPrefiltersOrTransports ,巧妙的是 transports 中的请求分发器在大部分参数设置完成后,也通过函数 inspectPrefiltersOrTransports 取到与请求类型匹配的请求分发器。

通过(右边代码一)我们可以看出来:

  1. 遍历 structure[dataType] 数组,并执行回调
  2. prefilterOrFactory 为函数数组元素,执行该函数如果返回的结果 dataTypeOrTransport 是字符串且时 prefilters 且没有被 inspected 过,就给 options.dataTypes 数组头部添加该字符串
  3. 继续递归dataTypeOrTransport(当我们使用 json/jsonp 的时候会返回“script”,于是会执行“script”相关的回调)
  4. 如果是 transport 就返回 dataTypeOrTransport 的假结果


前置过滤器 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)。

 

任务

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
  5. <script src="http://code.jquery.com/jquery-latest.js"></script>
  6. <title>模拟image的ajaxPrefilter与ajaxTransport处理</title>
  7. </head>
  8. <body>
  9.  
  10. <button id="test">模拟image的ajaxPrefilter与ajaxTransport处理</button>
  11. <ul></ul>
  12. <script type="text/javascript">
  13.  
  14. var $ul = $('ul')
  15.  
  16. function show(data) {
  17. $ul.append('<li>' + data + '</li>');
  18. }
  19.  
  20. //////////////////////////////////////////////////////////////////
  21. // options 是请求的选项 //
  22. // originalOptions 值作为提供给Ajax方法未经修改的选项,因此,没有ajaxSettings设置中的默认值 //
  23. // jqXHR 是请求的jqXHR对象 //
  24. //////////////////////////////////////////////////////////////////
  25. $.ajaxPrefilter("image", function(options, originalOptions, jqXHR) {
  26. //通过预处理器转化类型
  27. if (options.test) {
  28. options.type = 'GET'
  29. }
  30. //增加前缀
  31. options.url = "http://img1.sycdn.imooc.com//" + options.url
  32. });
  33.  
  34.  
  35. ///////////////////////
  36. // 请求分发器 transports //
  37. ///////////////////////
  38. $.ajaxTransport("image", function(s) {
  39. if (s.type === "GET" && s.async) {
  40. var image;
  41. return {
  42. send: function(_, callback) {
  43. image = new Image();
  44. function done(status) {
  45. if (image) {
  46. var statusText = (status == 200) ? "success" : "error",
  47. tmp = image;
  48. image = image.onreadystatechange = image.onerror = image.onload = null;
  49. callback(status, statusText, {
  50. image: tmp
  51. });
  52. }
  53. }
  54. image.onreadystatechange = image.onload = function() {
  55. done(200);
  56. };
  57. image.onerror = function() {
  58. done(404);
  59. };
  60. show(s.url)
  61. image.src = s.url;
  62. },
  63. abort: function() {
  64. if (image) {
  65. image = image.onreadystatechange = image.onerror = image.onload = null;
  66. }
  67. }
  68. };
  69. }
  70. });
  71.  
  72.  
  73. $("#test").click(function(){
  74.  
  75. //执行一个异步的HTTP(Ajax)的请求。
  76. var ajax = $.ajax({
  77. test : true, //测试
  78. url : '547d5a45000156f406000338-590-330.jpg',
  79. dataType : 'image',
  80. type : 'POST',
  81. data: {
  82. foo: ["bar1", "bar2"]
  83. },
  84. //这个对象用于设置Ajax相关回调函数的上下文
  85. context: document.body,
  86. //请求发送前的回调函数,用来修改请求发送前jqXHR
  87. beforeSend: function(xhr) {
  88. xhr.overrideMimeType("text/plain; charset=x-user-defined");
  89. show('局部事件beforeSend')
  90. },
  91. //请求完成后回调函数 (请求success 和 error之后均调用)
  92. complete: function() {
  93. show('局部事件complete')
  94. },
  95. error: function() {
  96. show('局部事件error请求失败时调用此函数')
  97. },
  98. success: function() {
  99. show('局部事件success')
  100. }
  101. })
  102.  
  103. ajax.done(function() {
  104. show('done')
  105. }).fail(function() {
  106. show('fail')
  107. }).always(function() {
  108. show('always')
  109. })
  110.  
  111.  
  112. })
  113.  
  114.  
  115.  
  116. </script>
  117. </body>
  118. </html>
下一节