资讯专栏INFORMATION COLUMN

一篇文章吃透Jsonp

seal_de / 908人阅读

摘要:前言最近因为工作的缘故,几乎把市面上所有库都下载了一遍,却发现没有百分百让我满意的,最后自己手动改写了,才符合了要求,也因此有了这篇文章。在这一点上不得不说做的很好,的函数包含了对各种情况的处理,还伪造了一个状态码的返回。

前言

最近因为工作的缘故,几乎把市面上所有Jsonp库都下载了一遍,却发现没有百分百让我满意的,最后自己手动改写了Jsonp,才符合了要求,也因此有了这篇文章。本文示例详实,代码简单,想弄明白Jsonp, 这一篇文章就够了。

什么是Jsonp

因为AJAX收到浏览器同源策略的限制,导致在跨域上有心无力,经常需要后台同学的帮助。而在浏览器中,所有带有src的标签都是不受同源策略限制的,如image, script。Jsonp上就是利用了script标签的这个特点,来实现跨域的。

其中,Jsonp和AJAX的原理完全不同,只不过Jquery带了个很不好的头,把两个东西封装在一起了,所以经常让新的同学混淆了。

Jsonp的原理:script src
AJAX的原理:xhr

举一个最简单的Jsonp的例子:





    
    
    
    Jsonp简单示例






http://www.qq.com/getJsonp?callback=jsonCallback这个链接返回的内容应该是

jsonpCallback({
    msg: success
})

这样,相当于后台调用了前台提前写好的callback函数,将要返回的数据当做callback函数的参数传入,这样前端就拿到后台传回来的数据了。

改写Jsonp

但是坦白来说,单纯的拿到数据并不能让我们满意。一个合适的请求函数,必然包含对成功、失败、超时的处理,就像我们上面写的那个简单示例,一旦出现异常,就不能让我们满意了。

在这一点上不得不说Jquery做的很好,Jquery的Jsonp函数包含了对各种情况的处理,还伪造了一个http状态码的返回。

Jsonp和AJAX不同,是拿不到状态码的,但是Jquery对于所有的错误都赋予了一个404的状态码,也是机智

对比其他的组件库(axios-jsonp, axios-jsonp-pro, jsonp, fetch=jsonp-es6), 要不就是完全没有对超时的处理,要不然就是把错误和超时混成一谭,更有甚者,有些都不能自定义callback函数的名字。这简直太过分了。

那我为什么不选择Jquery呢?因为太大了,webpack引入JQuery后瞬间大了80K, 而且多带带将Jsonp打包出来也有70K的样子,而我的源码只有20K,这是我不能接受的。

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免去了后台添加跨域头的烦恼,后台的改动较小,一次写好,终生受用(跨域头还要不断维护白名单)。这三种方案都有各自的使用场景,要在不同的场景进行恰当的选用,以上。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92627.html

相关文章

  • 前端20个真正灵魂拷问,吃透这些你就是中级前端工程师 【上篇】

    摘要:还是老规矩,从易到难吧传统的定时器,异步编程等。分配对象时,先是在空间中进行分配。内存泄漏内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 网上参差不弃的面试题,本文由浅入深,让你在...

    mdluo 评论0 收藏0
  • 前端20个真正灵魂拷问,吃透这些你就是中级前端工程师 【上篇】

    摘要:还是老规矩,从易到难吧传统的定时器,异步编程等。分配对象时,先是在空间中进行分配。内存泄漏内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 网上参差不弃的面试题,本文由浅入深,让你在...

    leap_frog 评论0 收藏0
  • 5分钟吃透React Native Flexbox

    摘要:在中是主流布局方式。它有三种状态正数零与负数。来看下运行效果。这是为正数的情况,如果,控件的大小就会根据设置的与来固定显示。如果发现生效的方式请务必告知。在中有主轴与副轴之分,主轴控制的排列方向,默认为。默认值为,继承父容器的属性。 今天我们来聊聊Flexbox,它是前端的一个布局方式。在React Native中是主流布局方式。如果你刚刚入门React Native,或者没有多少前端...

    developerworks 评论0 收藏0
  • 5分钟吃透React Native Flexbox

    摘要:在中是主流布局方式。它有三种状态正数零与负数。来看下运行效果。这是为正数的情况,如果,控件的大小就会根据设置的与来固定显示。如果发现生效的方式请务必告知。在中有主轴与副轴之分,主轴控制的排列方向,默认为。默认值为,继承父容器的属性。 今天我们来聊聊Flexbox,它是前端的一个布局方式。在React Native中是主流布局方式。如果你刚刚入门React Native,或者没有多少前端...

    Nekron 评论0 收藏0
  • 吃透动态代理,解密spring AOP源码(四)

    摘要:值得一提的是由于采用动态创建子类的方式生成代理对象,所以不能对目标类中的方法进行代理。动态代理中生成的代理类是子类,调试的时候可以看到,打开源码可看到实现了和也就实现方法。 前面讲到了动态代理的底层原理,接下来我们来看一下aop的动态代理.Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,一种是基于CGLib的动态代理. ①JDK动态代理:使用JDK创建代理有一个限制...

    Codeing_ls 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<