前言
我工作以来接触的第一个项目就是前后端分离的,前端静态文件有自己独立域名,通过接口来获取数据进行渲染等操作。
跨域的方法不需要多言,随便一搜,就有很多,但最常用不外乎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原理解析(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持小牛知识库。
我正在尝试使用我的RTX 2060 Super与Keras进行预测。出于某种原因,它似乎在我的CPU上运行。 这是我用来调试的测试脚本: 以下是打印到控制台的结果: 下面是一个屏幕截图,显示了我在Task Manager中的CPU和GPU利用率: 任何帮助都将不胜感激!
一、Android中的缓存策略 一般来说,缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的。当缓存满了之后,再想其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存。 因此LRU(Least Recently Used)缓存算法便应运而生,LRU是最近最少使用的算法,它的核
本文向大家介绍Java基础异常处理代码及原理解析,包括了Java基础异常处理代码及原理解析的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了java基础异常处理代码及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 异常的定义:中断了正常指令流的事件。 try..catch..finally结构: 输出结果: throw与thro
本文向大家介绍ajax和jsonp跨域的原理本质详解,包括了ajax和jsonp跨域的原理本质详解的使用技巧和注意事项,需要的朋友参考一下 为什么会有跨域问题? - 因为有同源策略 同源策略是浏览器的一种安全策略,所谓同源指的是 请求URL地址中的 协议, 域名 和 端口 都相同,只要其中之一不相同就是跨域 同源策略主要为了保证浏览器的安全性 在同源策略下,浏览器 不允许 Ajax跨域获取服务器数
本文向大家介绍JSONP的原理是什么?解决什么问题?相关面试题,主要包含被问及JSONP的原理是什么?解决什么问题?时的应答技巧和注意事项,需要的朋友参考一下 JS 动态插入 script 并将 src 指向后端 API,后台返回 json 并使用协定的 callback 函数把 json 包起来。浏览器以 JS 内容解析执行返回的内容,回调函数得以被调用并传入了返回的 json 对象。 解决的是
说到Seata中的配置管理,大家可能会想到Seata中适配的各种配置中心,其实今天要说的不是这个,虽然也会简单分析Seata和各配置中心的适配过程,但主要还是讲解Seata配置管理的核心实现 Server启动流程 在讲配置中心之前,先简单介绍一下Server端的启动流程,因为这一块就涉及到配置管理的初始化,核心流程如下: 程序入口在Server#main方法中 获取port的几种形式:从容器中获取