6-10 jsonp的实现
本节编程练习不计算学习进度,请电脑登录imooc.com操作

jsonp的实现

我们发送一个 jsonp 的请求:

$.ajax({
    crossDomain:true,//强制跨域
    url: ' http://url...’, //不同的域
    type: 'GET', // jsonp模式只有GET是合法的
    data: {
        'action': 'aaron'
    }, // 预传参的数组
    dataType: 'jsonp', // 数据类型
    jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来
})

通过 ajax 请求不同域的实现,jsonp 底层不是靠 XmlHttpRequest 而是 script,所以不要被这个方法给迷惑了。

这里有几个要注意的:

  1. 在 ajax 请求中类型如果是 type 是 post,其实内部都只会用 get,因为其跨域的原理就是用的动态加载 script 的 src,所以我们只能把参数通过 url 的方式传递
  2. 我们使用了 dataType 是 'jsonp' 但是 jquery 内部有进一步的优化,如果探测到还是同域下的请求,依然还是用 XmlHttpRequest 处理,所以我们在同域下测试的话,可以把 crossDomain 选项置为 true,这样强制为跨域处理,这样就会通过 script 处理了,那么根据 jsonp 的原理其实 jquery 内部会把 URL 最终会转化成:
http://192.168.1.113:8080/github/jQuery/jsonp.php?callback=flightHandler&action=aaron&_=1418782732584 ">

然后通过创建脚本动态加载:

<script type="text/javascript" src=" http://192.168.1.113:8080/github/jQuery/jsonp.php?callback=flightHandler&amp;action=aaron&amp;_=1418782732584 "></script>

然后 php 方就会收到 get 请求的参数,通过解析出 callback 执行 callback 这个回调并传递参数。

要处理的几个问题

1. 采用的是脚本请求的方法,所以虽然 dataType 是 'jsonp' 但是内部还是按照 script 处理
2. get 请求的后缀拼接,编码的处理
3. 避免缓存的处理

所以流程就会分二步:

  1. 针对 jsonp 的预处理,主要是转化拼接这些参数,然后处理缓存,因为 jsonp 的方式也是靠加载 script 所以要关闭浏览器缓存
  2. inspectPrefiltersOrTransports中jsonp 的预处理后,还要在执行 inspect(dataTypeOrTransport); 的递归,就是为了关闭这个缓存机制
  3. jquery 通过预处理会在 window 对象中加载一个全局的函数,当代码插入时函数执行,执行完毕后就会被移除。同时 jquery 还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的 Ajax 请求一样工作。


分发器执行代码

当我们所有的参数都转化好了,此时会经过请求发送器用来处理发送的具体,为什么会叫做分发器,因为发送的请求目标,ajax 因为参杂了 jsonp 的处理,所以实际上的请求不是通过 xhr.send(XmlHttpRequest) 发送的,而是通过 get 方式的脚本加载的,所以 transports 对象在初始化构件的时候,会生成 2 个处理器

*: Array[1]     针对xhr方式
script: Array[1]  针对script,jsonp方式

所以 transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR),那么得到的 transport 就会根据当前的处理的类型,来选择采用哪种发送器(*、script)所以最终的实现就是通过动态加载脚本!

 

任务

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
  5. <title>模拟jsonp的实现</title>
  6. <script src="http://code.jquery.com/jquery-latest.js"></script>
  7. </head>
  8. <body>
  9.  
  10. <button id="test">测试模拟jsonp的实现</button>
  11.  
  12. <script type="text/javascript">
  13.  
  14. function prescript(s) {
  15. if (s.cache === undefined) {
  16. s.cache = false;
  17. }
  18. if (s.crossDomain) {
  19. s.type = "GET";
  20. }
  21. }
  22.  
  23.  
  24. function prejsonp(s, originalSettings, jqXHR) {
  25. // 给回调函数命名
  26. var callbackName = s.jsonpCallback
  27. s.url += (/(?:)/.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
  28. // 脚本执行后使用数据转换器来检索json
  29. // 提供给代码获取服务器的是据
  30. s.getData = function() {
  31. if (!responseContainer) {
  32. jQuery.error(callbackName + " was not called");
  33. }
  34. return responseContainer[0];
  35. };
  36. //修改处理机制
  37. s.dataTypes[0] = "json";
  38. // 创建一个全局函数
  39. overwritten = window[callbackName];
  40. //用来收集服务器给的数据
  41. window[callbackName] = function() {
  42. responseContainer = arguments;
  43. };
  44.  
  45. return "script";
  46. }
  47.  
  48. /**
  49. * jsonp的预先处理
  50. */
  51. function inspectPrefiltersOrTransportsA(options, originalOptions, jqXHR) {
  52. //预处理jsonp
  53. var dataTypeOrTransport = prejsonp(options, originalOptions, jqXHR)
  54. //扩充dataTypes
  55. options.dataTypes.unshift(dataTypeOrTransport);
  56. //预处理script类型
  57. prescript(options)
  58. }
  59.  
  60.  
  61. /**
  62. * 分发器
  63. * @return {[type]} [description]
  64. */
  65. function inspectPrefiltersOrTransportsB(s, originalOptions, jqXHR) {
  66. return {
  67. send: function(_, complete) {
  68. var script = jQuery("<script>").prop({
  69. async: true,
  70. charset: s.scriptCharset,
  71. src: s.url
  72. }).on(
  73. "load error",
  74. callback = function(evt) {
  75. script.remove();
  76. callback = null;
  77. if (evt) {
  78. complete()
  79. }
  80. }
  81. );
  82. //<script async="" src="http://192.168.1.113:8080/github/jQuery/jsonp.php
  83. document.head.appendChild(script[0]);
  84. }
  85. }
  86. }
  87. /**
  88. * 模拟ajax的 jsonp请求
  89. * @param {[type]} options [description]
  90. * @return {[type]} [description]
  91. */
  92. function createAjax(options) {
  93. if (typeof url === "object") {
  94. options = url;
  95. url = undefined;
  96. }
  97. options = options || {};
  98. /**
  99. * 参数
  100. * jQuery.ajaxSetup 是默认参数
  101. * @type {[type]}
  102. */
  103. var s = jQuery.ajaxSetup({}, options);
  104. // Deferreds
  105. // 异步机制
  106. var deferred = jQuery.Deferred();
  107. var completeDeferred = jQuery.Callbacks("once memory");
  108. /**
  109. * 实际返回的ajax对象
  110. * @type {Object}
  111. */
  112. var jqXHR = {}
  113. // 把jqXHR对象转化promise对象,幷加入complete、success、error方法
  114. deferred.promise(jqXHR).complete = completeDeferred.add;
  115. //别名
  116. jqXHR.success = jqXHR.done;
  117. jqXHR.error = jqXHR.fail;
  118. s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(/(?:)/) || [""];
  119. //预处理
  120. inspectPrefiltersOrTransportsA(s, options, jqXHR);
  121. for (i in {
  122. success: 1,
  123. error: 1,
  124. complete: 1
  125. }) {
  126. jqXHR[i](s[i]);
  127. }
  128. /**
  129. * 分发器
  130. */
  131. transport = inspectPrefiltersOrTransportsB(s, options, jqXHR);
  132. function done(status, nativeStatusText, responses, headers) {
  133. console.log(s,s.getData())
  134. }
  135. //发送请求
  136. transport.send(s, done);
  137. return jqXHR;
  138. }
  139. function show(data){
  140. $('body').append('<li>'+ data +'</li>');
  141. }
  142.  
  143. /**
  144. * 数据回调接收
  145. * @return {[type]} [description]
  146. */
  147. function flightHandler(){
  148.  
  149. }
  150.  
  151. $("#test").click(function(){
  152. //执行一个异步的HTTP(Ajax)的请求。
  153. var ajax = createAjax({
  154. url: 'http://192.168.1.113:8080/github/jQuery/jsonp.php',
  155. data: {
  156. 'action': 'aaron'
  157. }, // 预传参的数组
  158. dataType: 'jsonp', // 数据类型
  159. jsonp: 'callback', // 指定回调函数名,与服务器端接收的一致,并回传回来
  160. jsonpCallback:flightHandler,
  161. success: function() {
  162. show('局部事件success')
  163. }
  164. })
  165. })
  166.  
  167.  
  168. </script>
  169. </body>
  170. </html>
下一节