前言

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

跨域的方法不需要多言,随便一搜,就有很多,但最常用不外乎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

  1. 《高性能JavaScript》
  2. npm上的一个jsonp实现,JSONP

JSONP原理解析的更多相关文章

  1. Ajax跨域:Jsonp原理解析

    推荐先看下这篇文章:JS跨域(ajax跨域.iframe跨域)解决方法及原理详解(jsonp) JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重 ...

  2. 【跨域】#001 JSONP原理解析【总结】

    一.JSONP 是什么? 1.1 概念 JSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料.由于同源策略,一般来说位于 server1 ...

  3. js跨域请求方式 ---- JSONP原理解析

    这篇文章主要介绍了js跨域请求的5中解决方式的相关资料,需要的朋友可以参考下     跨域请求数据解决方案主要有如下解决方法:   1 2 3 4 5 JSONP方式 表单POST方式 服务器代理 H ...

  4. JSONP跨域的原理解析( 一种脚本注入行为)

    JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制, 被称为“some-Origin Policy”(同源策略).这一策略对于Jav ...

  5. JSONP跨域的原理解析

    JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为“Same- Origin Policy”(同源策略).这一策略对于Jav ...

  6. 【转】JSONP跨域的原理解析

    JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略).这一策略对于Java ...

  7. JSONP跨域的原理解析(转)

    JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为"Same-Origin Policy"(同源策略). ...

  8. JSONP跨域的原理解析及其实现介绍

    JSONP跨域的原理解析及其实现介绍 作者: 字体:[增加 减小] 类型:转载 时间:2014-03-22 JSONP跨域GET请求是一个常用的解决方案,下面我们来看一下JSONP跨域是如何实现的,并 ...

  9. JSONP跨域的原理解析[转]

    转自 http://www.nowamagic.net/librarys/veda/detail/224 JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中, ...

随机推荐

  1. 使用vue-cli脚手架搭建简单项目框架

    1.首先已经安装了node,最好版本6以上. 2.安装淘宝镜像 大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像.这样就可以直接使用cnpm了. npm insta ...

  2. Gridview 动态添加行

    /// <summary> /// 首次加载绑定 /// </summary> private void DataLoad()         { list.Add(new P ...

  3. PHP设计模式四:适配器模式

    一.什么是适配器模式 适配器模式有两种:类适配器模式和对象适配器模式.其中类适配器模式使用继承方式,而对象适配器模式使用组合方式.由于类适配器 模式包含双重继承,而PHP并不支持双重继承,所以一般都采 ...

  4. LeetCode 62. Unique Paths(所有不同的路径)

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  5. ssm框架的整合

    首先创建一个web工程,我这里使用的IDE为eclipse. 结果目录如下: 添加相关的jar包: 接下来是完成配置文件 首先我们先配置web.xml: <?xml version=" ...

  6. BZOJ-1225-[HNOI2001] 求正整数

    Description 对于任意输入的正整数n,请编程求出具有n个不同因子的最小正整数m.例如:n=4,则m=6,因为6有4个不同整数因子1,2,3,6:而且是最小的有4个因子的整数. Input n ...

  7. request的getServletPath(),getContextPath(),getRequestURI(),getRealPath("/")区别

    假定你的web application 名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果 ...

  8. 使用缓存Memcache存储access_token

    接上篇文本,千辛万苦终于拿到了access_token. 正常情况下access_token有效期为7200秒,重复获取将导致上次获取的access_token失效.目前,获取access_token ...

  9. Jquery datatable中文排序问题

    先扩展datatable的的排序功能,添加一个自定义排序函数 //为jq datatable 自定义中文排序 jQuery.fn.dataTableExt.oSort['chinese-sort-as ...

  10. 如何让Vim成为我们的神器

    Vim 是 Linux 系统上的最著名的文本/代码编辑器,也是早年的 Vi 编辑器的加强版,而 gVim 则是其 Windows 版.它的最大特色是完全使用键盘命令进行编辑,脱离了鼠标操作虽然使得入门 ...