基于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 相关文章推荐
创建一个复制UBB软件信息的链接或按钮的js代码
Jan 06 Javascript
Javascript document.referrer判断访客来源网址
May 15 Javascript
JQuery记住用户名密码实现下次自动登录功能
Apr 27 Javascript
js识别uc浏览器的代码
Nov 06 Javascript
一篇文章掌握RequireJS常用知识
Jan 26 Javascript
AngularJS入门教程之数据绑定用法示例
Nov 01 Javascript
jQuery按需加载轮播图(web前端性能优化)
Feb 17 Javascript
JS+Ajax实现百度智能搜索框
Aug 04 Javascript
jQuery+vue.js实现的九宫格拼图游戏完整实例【附源码下载】
Sep 12 jQuery
Bootstrap popover 实现鼠标移入移除显示隐藏功能方法
Jan 24 Javascript
Angular使用操作事件指令ng-click传多个参数示例
Mar 27 Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
Jun 03 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 checkbox复选框值的获取与checkbox默认值输出方法
2010/05/15 PHP
PHP错误提示的关闭方法详解
2013/06/23 PHP
javascript 极速 隐藏/显示万行表格列只需 60毫秒
2009/03/28 Javascript
通过jQuery源码学习javascript(一)
2012/12/27 Javascript
javascript实现yield的方法
2013/11/06 Javascript
jQuery中:radio选择器用法实例
2015/01/03 Javascript
TypeScript 学习笔记之基本类型
2015/06/19 Javascript
iframe跨域通信封装详解
2015/08/11 Javascript
基于jquery实现即时检查格式是否正确的表单
2016/05/06 Javascript
Windows环境下npm install 报错: operation not permitted, rename的解决方法
2016/09/26 Javascript
ASP.NET jquery ajax传递参数的实例
2016/11/02 Javascript
详解jquery easyui之datagrid使用参考
2016/12/05 Javascript
jQuery插件FusionCharts实现的2D柱状图效果示例【附demo源码下载】
2017/03/06 Javascript
js实现一个猜数字游戏
2017/03/31 Javascript
教你5分钟学会用requirejs(必看篇)
2017/07/25 Javascript
Webpack 服务器端代码打包的示例代码
2017/09/19 Javascript
Three.js入门之hello world以及如何绘制线
2017/09/25 Javascript
详解Node全局变量global模块
2017/09/28 Javascript
脚手架vue-cli工程webpack的基本用法详解
2018/09/29 Javascript
微信小程序实现购物页面左右联动
2019/02/15 Javascript
JS使用队列对数组排列,基数排序算法示例
2019/03/02 Javascript
[01:30:54]《加油DOTA》 第三期
2014/08/18 DOTA
Python list操作用法总结
2015/11/10 Python
Python使用正则表达式抓取网页图片的方法示例
2017/04/21 Python
python实现爬虫抓取小说功能示例【抓取金庸小说】
2019/08/09 Python
Django之腾讯云短信的实现
2020/06/12 Python
使用keras时input_shape的维度表示问题说明
2020/06/29 Python
Python绘制组合图的示例
2020/09/18 Python
python中_del_还原数据的方法
2020/12/09 Python
10种CSS3实现的loading动画,挑一个走吧?
2020/11/16 HTML / CSS
Visual-Click葡萄牙:欧洲领先的在线眼镜商
2020/02/17 全球购物
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
2015/02/22 面试题
2015年国税春训心得体会
2015/03/09 职场文书
nginx搭建图片服务器的过程详解(root和alias的区别)
2021/03/31 Servers
为了顺利买到演唱会的票用Python制作了自动抢票的脚本
2021/10/16 Python
springboot创建的web项目整合Quartz框架的项目实践
2022/06/21 Java/Android