基于JSONP原理解析(推荐)


Posted in Javascript onDecember 04, 2017

前言

我工作以来接触的第一个项目就是前后端分离的,前端静态文件有自己独立域名,通过接口来获取数据进行渲染等操作。

跨域的方法不需要多言,随便一搜,就有很多,但最常用不外乎jsonp和CORS。jsonp着重于前端,也算是前端Hack技巧,CORS重于后端,服务端需要配置的地方会较多。

这篇解析一下jsonp的实现原理。

基本原理

基本原理很容易说明白,在html页面中有一些标签是不受跨域限制的,比如img,script,link等。如果把我们需要的数据,放在一个js文件里面,这时,我们就能突破浏览器同源的限制。

创建script标签

《高性能JavaScript》中提到了动态脚本元素,作者写道:

1、文件在该元素被添加到页面时开始下载。这种技术的重点在于:无论何时启动下载,文件的下载和执行过程不会阻塞页面其他进程。

2、使用动态脚本节点下载文件时,返回的代码通常会立刻执行(除了Firefox和Oprea,它们会等待此前所有动态脚本节点执行完毕。)当脚本自执行时,这种机制运行正常。

引用1保证了JSONP请求的时候不会阻塞主线程,引用2保证了JSONP代码在加载完成后,立刻自执行时不会出错。

callback

服务端在接收到GET请求之后,通常要判断是否有callback参数,如果有,则需要在返回的数据外面加上一个方法名和括号。例如,发起如下请求:

http://www.a.com/getSomething?callback=jsonp0

那么服务端就会返回如下内容:

jsonp0({code:200,data:{}})

很明显,由于这是在动态加载的Script标签中包含的内容,那么这就是一段自执行代码,这段代码只有一个函数被调用———jsonp0。

当然,有执行,则必须先创建,否则就会报错。创建这一步,就需要在调用前执行。

具体实现如下:

function jsonp (url, successCallback, errorCallback, completeCallback) {

 // 声明对象,需要将函数声明至全局作用域
 window.jsonp0 = function (data) {
  successCallback(data);
  if (completeCallback) completeCallback();
 }
 // 创建script标签,并将url后加上callback参数
 var 
  script = document.createElement('script')
  , url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=jsonp0'
 ;
 script.src = url;
 document.head.parentNode.insertBefore(script, document.head);
 // 等到script加载完毕以后,就会自己执行
}

上面基本上完成了一个jsonp方法的核心,此时,jsonp0是我们声明好的一个函数,如果服务端正常回传的时候,就会执行jsonp0函数,里面的successCallback回调也会执行。

完善一下

在实际情况下,通常会有许多个jsonp的请求同时调用,

那么既然jsonp0就能满足我们的需要,为什么常常看到jsonp1,jsonp2等等依次累加的代码呢?

这是因为,请求可能是很多个异步进行。在第一次执行jsonp方法时,window.jsonp0是函数A,此时去加载js文件,在js未加载完毕的情况下,又调用了一次jsonp方法,此时,window.jsonp0指向了函数B。那么等到两次的js加载完毕以后,都会执行第二次的回调。

所以,我们需要对callback的名字做一个区别处理,累加就能满足需要。

修改一下代码:

var jsonpCounter = 0;
function jsonp (url, successCallback, errorCallback, completeCallback) {
 
 var jsId = 'jsonp' + jsonpCounter++;
 
 // 声明对象,需要将函数声明至全局作用域
 window[jsId] = function (data) {
  successCallback(data);
  if (completeCallback) completeCallback();
  clean();
 }
 // 创建script标签,并将url后加上callback参数
 var 
  script = document.createElement('script')
  , url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=' + jsId
 ;
 script.src = url;
 document.head.parentNode.insertBefore(script, document.head);
 // 等到script加载完毕以后,就会自己执行
 
 //在执行完我们这个方法以后,会有很多script标签出现在head之前,我们需要手动的删除掉他们。
 function clean () {
  script.parentNode.removeChild(script);
  window[jsId] = function(){};
 }
}

加入了累加和清理之后,还有一个重要的地方需要处理,就是错误回调。正常来说,我们通常请求jsonp时,会设定一个超时时间,如果超过这个时间以后,就抛出超时异常。

实现如下:

var jsonpCounter = 0;
function jsonp (url, successCallback, errorCallback, completeCallback, timeout) {
 // 略去上面写过的代码
 var 
  timeout = timeout || 10000
  , timer
 ;
 if (timeout) {
  timer = setTimeout(function () {
   if (errorCallback) {
    errorCallback(new Error('timeout'));
   }
   clean();
  }, timeout)
 }
 
 function clean () {
  script.parentNode.removeChild(script);
  window[jsId] = function(){};
  if (timer) clearTimeout(timer);
 }
}

这样,基本上就完成了jsonp的全部功能,剩下的可能需要做一些兼容的修改,才算是一个完整的jsonp方法。

REFER

《高性能JavaScript》

npm上的一个jsonp实现,JSONP

以上这篇基于JSONP原理解析(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery实现点击弹出带标题栏的弹出层(从右上角飞入)效果
Sep 19 Javascript
浅析jQuery Mobile的初始化事件
Dec 03 Javascript
AngularJS使用ngOption实现下拉列表的实例代码
Jan 23 Javascript
AngularJS使用angular.bootstrap完成模块手动加载的方法分析
Jan 19 Javascript
jQuery实现 RadioButton做必选校验功能
Jun 15 jQuery
AngularJS解决ng-if中的ng-model值无效的问题
Jun 21 Javascript
js HTML5 canvas绘制图片的方法
Sep 08 Javascript
基于vue开发微信小程序mpvue-docs跳转页面功能
Apr 10 Javascript
vue 对象添加或删除成员时无法实时更新的解决方法
May 01 Javascript
JavaScript中使用Spread运算符的八种方法总结
Jun 18 Javascript
js实现点击上传图片并设为模糊背景
Aug 02 Javascript
Java 生成随机字符的示例代码
Jan 13 Javascript
利用Javascript获取选择文本所在的句子详解
Dec 03 #Javascript
微信小程序图片选择区域裁剪实现方法
Dec 02 #Javascript
vue中eventbus被多次触发以及踩过的坑
Dec 02 #Javascript
Angular之toDoList的实现代码示例
Dec 02 #Javascript
React Native 使用Fetch发送网络请求的示例代码
Dec 02 #Javascript
vue微信分享 vue实现当前页面分享其他页面
Dec 02 #Javascript
Vue按需加载的具体实现
Dec 02 #Javascript
You might like
用Php实现链结人气统计
2006/10/09 PHP
php4的session功能评述(一)
2006/10/09 PHP
php去掉字符串的最后一个字符附substr()的用法
2011/03/23 PHP
使用PHP和HTML5 FormData实现无刷新文件上传教程
2014/09/06 PHP
php使用str_replace替换多维数组的实现方法分析
2017/06/15 PHP
thinkphp框架page类与bootstrap分页(美化)
2017/06/25 PHP
Laravel 5.5 实现禁用用户注册示例
2019/10/24 PHP
jQuery在IE下使用未闭合的xml代码创建元素时的Bug介绍
2012/01/10 Javascript
JavaScript用JQuery呼叫Server端方法示例代码
2014/09/03 Javascript
js使用cookie记录用户名的方法
2015/11/26 Javascript
基于JavaScript实现表单密码的隐藏和显示出来
2016/03/02 Javascript
原生 JS Ajax,GET和POST 请求实例代码
2016/06/08 Javascript
JavaScript动态检验密码强度的实现方法
2016/11/09 Javascript
将JSON字符串转换成Map对象的方法
2016/11/30 Javascript
Node.js Mongodb 密码特殊字符 @的解决方法
2017/04/11 Javascript
基于jquery日历价格、库存等设置插件
2020/07/05 jQuery
antd多选下拉框一行展示的实现方式
2020/10/31 Javascript
antd中table展开行默认展示,且不需要前边的加号操作
2020/11/02 Javascript
[49:20]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
[01:18:36]LGD vs VP Supermajor 败者组决赛 BO3 第一场 6.10
2018/07/04 DOTA
使用Python的Flask框架构建大型Web应用程序的结构示例
2016/06/04 Python
Python中的函数式编程:不可变的数据结构
2018/10/08 Python
python实现证件照换底功能
2019/08/20 Python
python 动态调用函数实例解析
2019/10/21 Python
Python Numpy 自然数填充数组的实现
2019/11/28 Python
FitFlop美国官网:英国符合人体工学的鞋类品牌
2018/10/05 全球购物
微软加拿大官方网站:Microsoft Canada
2019/04/28 全球购物
电信专业应届生自荐信
2013/09/28 职场文书
广告语设计及教案
2014/03/21 职场文书
小学学雷锋活动总结
2014/04/25 职场文书
2014年教师政治学习材料
2014/06/02 职场文书
2014年学校领导班子对照检查材料
2014/09/19 职场文书
党员评议个人总结
2014/10/20 职场文书
2014年党的群众路线活动个人整改措施
2014/10/28 职场文书
《平行四边形的面积》教学反思
2016/02/16 职场文书
单机多实例部署 MySQL8.0.20
2022/05/15 MySQL