最近由于工做的缘故,几乎把市面上全部Jsonp库都下载了一遍,却发现没有百分百让我满意的,最后本身手动改写了Jsonp,才符合了要求,也所以有了这篇文章。本文示例详实,代码简单,想弄明白Jsonp, 这一篇文章就够了。javascript
由于AJAX收到浏览器同源策略的限制,致使在跨域上有心无力,常常须要后台同窗的帮助。而在浏览器中,全部带有src的标签都是不受同源策略限制的,如image, script。Jsonp上就是利用了script标签的这个特色,来实现跨域的。html
其中,Jsonp和AJAX的原理彻底不一样,只不过Jquery带了个很很差的头,把两个东西封装在一块儿了,因此常常让新的同窗混淆了。前端
Jsonp的原理:script src
AJAX的原理:xhr
举一个最简单的Jsonp的例子:java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Jsonp简单示例</title> </head> <body> </body> <script type="text/javascript"> // Jsonp回调 function jsonCallback(data) { console.log(data); } //添加<script>标签的方法 function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type", "text/javascript"); script.src = src; document.body.appendChild(script); } // 向后台发起请求(连接是胡乱写的) addScriptTag('http://www.qq.com/getJsonp?callback=jsonCallback') </script> </html>
http://www.qq.com/getJsonp?callback=jsonCallback
这个连接返回的内容应该是node
jsonpCallback({ msg: success })
这样,至关于后台调用了前台提早写好的callback函数,将要返回的数据当作callback函数的参数传入,这样前端就拿到后台传回来的数据了。webpack
可是坦白来讲,单纯的拿到数据并不能让咱们满意。一个合适的请求函数,必然包含对成功、失败、超时的处理,就像咱们上面写的那个简单示例,一旦出现异常,就不能让咱们满意了。ios
在这一点上不得不说Jquery作的很好,Jquery的Jsonp函数包含了对各类状况的处理,还伪造了一个http状态码的返回。es6
Jsonp和AJAX不一样,是拿不到状态码的,可是Jquery对于全部的错误都赋予了一个404的状态码,也是机智
对比其余的组件库(axios-jsonp, axios-jsonp-pro, jsonp, fetch=jsonp-es6), 要不就是彻底没有对超时的处理,要否则就是把错误和超时混成一谭,更有甚者,有些都不能自定义callback函数的名字。这简直太过度了。web
那我为何不选择Jquery呢?由于太大了,webpack引入JQuery后瞬间大了80K, 并且单独将Jsonp打包出来也有70K的样子,而个人源码只有20K,这是我不能接受的。npm
jsonp这个组件的问题是没有对错误的处理,理解了Jsonp的原理,咱们能很容易的添加上这块的逻辑,如下是添加后的源码:
/** * Module exports. */ module.exports = jsonp; /** * Callback index. */ var count = 0; /** * Noop function. */ function noop(){} /** * JSONP handler * * Options: * - param {String} qs parameter (`callback`) * - prefix {String} qs parameter (`__jp`) * - name {String} qs parameter (`prefix` + incr) * - timeout {Number} how long after a timeout error is emitted (`60000`) * * @param {String} url * @param {Object|Function} optional options / callback * @param {Function} optional callback */ function jsonp(url, opts, fn){ if ('function' == typeof opts) { fn = opts; opts = {}; } if (!opts) opts = {}; var prefix = opts.prefix || '__jp'; // use the callback name that was passed if one was provided. // otherwise generate a unique name by incrementing our counter. var id = opts.name || (prefix + (count++)); var param = opts.param || 'callback'; var timeout = null != opts.timeout ? opts.timeout : 60000; var enc = encodeURIComponent; var target = document.getElementsByTagName('script')[0] || document.head; var script; var timer; if (timeout) { timer = setTimeout(function(){ cleanup(); if (fn) fn(new Error('Timeout')); }, timeout); } function cleanup(){ if (script.parentNode) script.parentNode.removeChild(script); window[id] = noop; if (timer) clearTimeout(timer); } function cancel(){ if (window[id]) { cleanup(); } } window[id] = function(data){ cleanup(); if (fn) fn(null, data); }; // add qs component url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id); url = url.replace('?&', '?'); // create script script = document.createElement('script'); script.src = url; // 添加对错误的处理 script.onerror = function (evt) { if (fn) fn(new Error('Error')); if (timer) clearTimeout(timer) } target.parentNode.insertBefore(script, target); return cancel; }
由于大部分是人家的代码,我也就不班门弄斧了,有须要的能够直接npm install jsonp, 而后比对node_modules/jsonp/index.js进行修改;有须要对Jsonp有更详细的处理的,也能够在个人基础上继续添加。
Jsonp的本质就是建立一个回调函数,而后在远程服务上调用这个函数而且将JSON数据形式做为参数传递,完成回调。比起另外两种后台无感知的跨域方案:image src、fetch no-cor,Jsonp能够对错误和超时进行处理,也能对后台返回的数据进行分析;而对于AJAX,Jsonp免去了后台添加跨域头的烦恼,后台的改动较小,一次写好,终生受用(跨域头还要不断维护白名单)。这三种方案都有各自的使用场景,要在不一样的场景进行恰当的选用,以上。