本文深入解析JSONP跨域请求的实现原理,利用script标签不受同源策略限制的特性,通过动态创建脚本节点加载数据。文章提供了从基础到完善的原生JavaScript代码示例,涵盖回调函数命名、异步处理及超时错误机制,帮助开发者理解并实现JSONP功能。
JSONP跨域原理与原生JavaScript实现详解
前言
在我参与的项目中,首个涉及前后端分离架构,前端静态资源部署于独立域名,通过接口获取数据以完成页面渲染等交互操作。
关于跨域解决方案,常见手段包括JSONP和CORS。JSONP主要侧重于前端实现,属于前端的一种Hack技巧;而CORS则更依赖后端配置,服务端需进行较多设置。
本文将重点解析JSONP的具体实现原理。
JSONP基本原理
基本原理
原理阐述较为直观:HTML中部分标签如img、script、link等不受浏览器同源策略限制。若将所需数据封装在JS文件中,即可突破同源限制。
动态创建Script标签
创建script标签
《高性能JavaScript》一书中提及动态脚本元素,作者指出:
1、文件在元素加入页面时开始下载。该技术核心在于:无论何时启动下载,文件的下载与执行过程均不阻塞页面其他进程。
2、使用动态脚本节点下载文件时,返回的代码通常会立即执行(Firefox和Opera除外,它们会等待此前所有动态脚本节点执行完毕)。当脚本自执行时,此机制运行正常。
引用1确保了JSONP请求不会阻塞主线程,引用2保证了JSONP代码在加载完成后能立即自执行且不出错。
Callback参数处理
callback
服务端接收GET请求后,通常需检查是否存在callback参数。若存在,则需在返回数据外层包裹方法名及括号。例如,发起如下请求:
http://www.a.com/getSomething?callback=jsonp0
服务端将返回如下内容:
jsonp0({code:200,data:{}})
显然,由于该内容包含在动态加载的Script标签中,因此这是一段自执行代码,仅调用了一个函数——jsonp0。
当然,有执行必先创建,否则将报错。创建步骤需在调用前执行。
基础JSONP实现代码
具体实现如下:
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功能
完善一下
实际场景中,通常存在多个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原理解析(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。