我们发送一个 jsonp 的请求:
$.ajax({ crossDomain:true,//强制跨域 url: ' http://url...’, //不同的域 type: 'GET', // jsonp模式只有GET是合法的 data: { 'action': 'aaron' }, // 预传参的数组 dataType: 'jsonp', // 数据类型 jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来 })
通过 ajax 请求不同域的实现,jsonp 底层不是靠 XmlHttpRequest 而是 script,所以不要被这个方法给迷惑了。
这里有几个要注意的:
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&action=aaron&_=1418782732584 "></script>
然后 php 方就会收到 get 请求的参数,通过解析出 callback 执行 callback 这个回调并传递参数。
要处理的几个问题:
1. 采用的是脚本请求的方法,所以虽然 dataType 是 'jsonp' 但是内部还是按照 script 处理 2. get 请求的后缀拼接,编码的处理 3. 避免缓存的处理
所以流程就会分二步:
分发器执行代码:
当我们所有的参数都转化好了,此时会经过请求发送器用来处理发送的具体,为什么会叫做分发器,因为发送的请求目标,ajax 因为参杂了 jsonp 的处理,所以实际上的请求不是通过 xhr.send(XmlHttpRequest) 发送的,而是通过 get 方式的脚本加载的,所以 transports 对象在初始化构件的时候,会生成 2 个处理器
*: Array[1] 针对xhr方式 script: Array[1] 针对script,jsonp方式
所以 transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR),那么得到的 transport 就会根据当前的处理的类型,来选择采用哪种发送器(*、script)所以最终的实现就是通过动态加载脚本!
<!doctype html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> <title>模拟jsonp的实现</title> <script src="http://code.jquery.com/jquery-latest.js"></script> </head> <body> <button id="test">测试模拟jsonp的实现</button> <script type="text/javascript"> function prescript(s) { if (s.cache === undefined) { s.cache = false; } if (s.crossDomain) { s.type = "GET"; } } function prejsonp(s, originalSettings, jqXHR) { // 给回调函数命名 var callbackName = s.jsonpCallback s.url += (/(?:)/.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName; // 脚本执行后使用数据转换器来检索json // 提供给代码获取服务器的是据 s.getData = function() { if (!responseContainer) { jQuery.error(callbackName + " was not called"); } return responseContainer[0]; }; //修改处理机制 s.dataTypes[0] = "json"; // 创建一个全局函数 overwritten = window[callbackName]; //用来收集服务器给的数据 window[callbackName] = function() { responseContainer = arguments; }; return "script"; } /** * jsonp的预先处理 */ function inspectPrefiltersOrTransportsA(options, originalOptions, jqXHR) { //预处理jsonp var dataTypeOrTransport = prejsonp(options, originalOptions, jqXHR) //扩充dataTypes options.dataTypes.unshift(dataTypeOrTransport); //预处理script类型 prescript(options) } /** * 分发器 * @return {[type]} [description] */ function inspectPrefiltersOrTransportsB(s, originalOptions, jqXHR) { return { send: function(_, complete) { var script = jQuery("<script>").prop({ async: true, charset: s.scriptCharset, src: s.url }).on( "load error", callback = function(evt) { script.remove(); callback = null; if (evt) { complete() } } ); //<script async="" src="http://192.168.1.113:8080/github/jQuery/jsonp.php document.head.appendChild(script[0]); } } } /** * 模拟ajax的 jsonp请求 * @param {[type]} options [description] * @return {[type]} [description] */ function createAjax(options) { if (typeof url === "object") { options = url; url = undefined; } options = options || {}; /** * 参数 * jQuery.ajaxSetup 是默认参数 * @type {[type]} */ var s = jQuery.ajaxSetup({}, options); // Deferreds // 异步机制 var deferred = jQuery.Deferred(); var completeDeferred = jQuery.Callbacks("once memory"); /** * 实际返回的ajax对象 * @type {Object} */ var jqXHR = {} // 把jqXHR对象转化promise对象,幷加入complete、success、error方法 deferred.promise(jqXHR).complete = completeDeferred.add; //别名 jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(/(?:)/) || [""]; //预处理 inspectPrefiltersOrTransportsA(s, options, jqXHR); for (i in { success: 1, error: 1, complete: 1 }) { jqXHR[i](s[i]); } /** * 分发器 */ transport = inspectPrefiltersOrTransportsB(s, options, jqXHR); function done(status, nativeStatusText, responses, headers) { console.log(s,s.getData()) } //发送请求 transport.send(s, done); return jqXHR; } function show(data){ $('body').append('<li>'+ data +'</li>'); } /** * 数据回调接收 * @return {[type]} [description] */ function flightHandler(){ } $("#test").click(function(){ //执行一个异步的HTTP(Ajax)的请求。 var ajax = createAjax({ url: 'http://192.168.1.113:8080/github/jQuery/jsonp.php', data: { 'action': 'aaron' }, // 预传参的数组 dataType: 'jsonp', // 数据类型 jsonp: 'callback', // 指定回调函数名,与服务器端接收的一致,并回传回来 jsonpCallback:flightHandler, success: function() { show('局部事件success') } }) }) </script> </body> </html>