微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

深入浅出jsonp

2015-07-16 20:27 前端大全

(点击上方,可快速关注)


作者:韩子迟

网址:http://www.cnblogs.com/zichi/p/4593047.html


前言


第一次听说jsonp,其实早在2年之前。当时在做一个活动页面的抽奖模块,要从服务端get一个概率,当时什么都不懂,同事说用ajax,我就用ajax,同事说dataType改成jsonp,我就改成jsonp。于是乎活动页面做完了,以后也没有碰到过jsonp,在这期间我一直以为jsonp跟ajax息息相关,是xhr的一种特殊的跨域形式…直到一个月前的一次面试,问到jsonp我被虐成狗,才决定看下jsonp,好吧,原来jsonp也不是很难。


为什么要用jsonp?


相信大家对跨域一定不陌生,对同源策略也同样熟悉。什么,你没听过?没关系,既然是深入浅出,那就从头说起。


假如我写了个index页面,页面里有个请求,请求的是一个json数据(不知道json数据的猛戳JSON简介以及用法汇总),简单思考写下如下代码:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

$.ajax({

url: 'http://localhost/a.json',

dataType: "json",

success: function (data) {

console.log(data);

}

})

</script>


{

"name": "hanzichi",

"age": 10

}


楼主把两个文件都放在wamp下的www文件夹下,ajax请求没有跨域,完美得到结果:




但是如果我的json文件和index文件不在一个域下,即跨域(不懂跨域的可参考JavaScript 的同源策略)了呢?


试着在wamp下新开个apache端口(不知道怎么开的可参考WampServer下使用多端口访问),将json文件放到该服务端口的文件夹下(楼主设置的端口号为8080,默认的是80端口),试着发送请求:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

$.ajax({

url: 'http://localhost:8080/a.json',

dataType: "json",

success: function (data) {

console.log(data);

}

})

</script>



很显然,提示跨域了!怎么搞?这时jsonp就要出马了!


神奇的script标签


与jsonp息息相关的是script标签,而xhr或者说传统意义上的ajax与之没有半毛钱关系!


接着看上面的index.html代码,我们看到页面引用了百度cdn的jquery路径,对于这样的方式我们似乎已经习以为常,但是仔细一想,script标签可是完完全全的跨域的啊…没错,jsonp的实现核心就是利用script标签的跨域能力!于是我们灵机一动,似乎可以这么搞,动态生成一个script标签,把json的url赋值给script的src属性,然后再把这个script标签插入dom里…


<body>

<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

var s = document.createElement('script');

s.src = 'http://localhost:8080/a.json';

document.body.appendChild(s);

</script>

</body>


我们创建了一个script标签,而标签内包裹的内容正是需要的json数据,但是报错如下:



原因是因为json数据并不是合法的js语句,把上面的json数据放在一个回调函数中是最简单的方法:


<body>

<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

function jsonpcallback(json) {

console.log(json);

}


var s = document.createElement('script');

s.src = 'http://localhost:8080/a.json';

document.body.appendChild(s);

</script>

</body>


jsonpcallback({

"name": "hanzichi",

"age": 10

});



当然,这时的a.json文件并不一定要这样命名,改成a.js也不会有一点问题。


而如果是与服务端交互也是一样的道理,比如和php:


<body>

<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

function jsonpcallback(json) {

console.log(json);

}


var s = document.createElement('script');

s.src="http://localhost:8080/test.php?callback=jsonpcallback";

document.body.appendChild(s);

</script>

</body>


<?php

$jsondata = '{

"name": "hanzichi",

"age": 10

}';

echo $_GET['callback'].'('.$jsondata.')';

?>


需要注意的是,jsonp提供的url(即动态生成的script标签的src),无论看上去是什么形式,最终生成返回的都是一段js代码。


JQuery对jsonp的封装


为了便于开发,jq对jsonp也进行了封装,封装在了ajax方法中。


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

$.ajax({

url: 'http://localhost:8080/a.json',

dataType: 'jsonp',

jsonpCallback: 'CallBack',

success: function (data) {

console.log(data);

}

});

</script>


CallBack({

"name": "hanzichi",

"age": 10

});


以上代码是针对请求文件中写死了callback函数名的情况。因为请求的是json文件,json不是服务器端的动态语言不能进行解析,如果是php或者其他的服务器端语言,则不用写死函数名,比如下面这样:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

<script type="text/javascript">

$.ajax({

url: 'http://localhost:8080/test.php',

dataType: 'jsonp',

success: function (data) {

console.log(data);

}

});

</script>


<?php

$jsondata = '{

"name": "hanzichi",

"age": 10

}';

echo $_GET['callback'].'('.$jsondata.')';

?>


当然类似的封装好的方法还有几种:


// 1

$.getJSON("http://localhost:8080/test.php?callback=?", function(data) {

console.log(data);

});


// 2

$.get('http://localhost:8080/test.php', function(data) {

console.log(data);

}, 'jsonp');


需要注意的是getJSON方法的请求地址url需要带上callback=?,因为jq对该方法进行封装的时候并没有默认回调函数变量名为callback,于是php中$_GET['callback']就找不到变量值了。


而一般的jq方法url 中不用指定 callback 参数。对于 jQuery 中的 jsonp 来说,callback 参数是自动添加的。默认情况下,jQuery 生成的 jsonp 请求中 callback 参数是形如 callback=jQuery200023559735575690866_1434954892929 这种根据看似随机的名字,对应的就是 success 那个处理函数,所以一般不用特意处理。二如果要写死callback名的话,可以参照上文。



总结


由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求,这就是jsonp的核心。


jsonp原理:


  1. 首先在客户端注册一个callback, 然后把callback的名字传给服务器。

  2. 服务器先生成 json 数据。 然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp. 最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

  3. 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)




前端大全

微信号:FrontDev

打造东半球最好的 前端技术 微信号

--------------------------------------

商务合作QQ:2302462408

招聘和猎头服务QQ:2302462408

投稿网址:top.jobbole.com





 
前端大全 更多文章 5个典型的JavaScript面试题(上) Limu:JavaScript的那些书 Web开发:我希望得到的编程学习路线图 JavaScript基础工具清单 常用排序算法之JavaScript实现
猜您喜欢 一个优秀数据分析师应该具备哪些特性? 世界上最酷的程序员 互联网公司技术分享形式的探讨 MySQL半同步复制的数据一致性探讨 智汇二期丨线下交流会报名开启