我們都知道使用<script>標籤可以引入外部的JS檔案,即使這個JS檔案來自於其他的網站,比如我們引用存放在網路伺服器上的jQuery框架。在這個過程中,我們已經實現跨域訪問。像<script>標籤這種本身具有跨域訪問能力的標籤還有<link>、<img>、<iframe>等。jsonp的實現原理就是利用<script>標籤,實現了跨域訪問。
先看一個例子:
先建立一個html檔案,程式碼如下:
<body>
<p>名字:<span id="name"></span></p>
<script>
function test(obj) {
document.getElementById("name").innerHTML = obj.name;
}
</script>
</body>
此時在瀏覽器開啟這個檔案,頁面中只有“名字:”這三個字元。
現在建立一個JS檔案,test.js,其中的程式碼如下:
var person = {
name: "Jack",
age: 20
}
test(person);
通過<script>標籤在html檔案中引入這個test.js檔案(新增到body結束標籤之前):
<script src="test.js"></script>
再在瀏覽器中開啟html檔案就會看到:
如果把這個test.js放在百度的伺服器上,在淘寶的頁面上有一個test函式宣告,然後動態地建立一個<script>標籤來引用這個test.js,那麼在淘寶的頁面上就可以顯示來自百度的伺服器的資料,這就是jsonp的原理。當然引用的不一定非要是js檔案,也可以是php、.net等檔案,只要在這些檔案中事先寫好相應的程式碼即可。
下面用聚合資料的天氣預報介面和新浪的城市查詢介面為例來介紹jsonp。
首先是新浪的城市查詢介面。
新浪的城市查詢介面是:
http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js
可以直接在瀏覽器中開啟,發現是一個名為remote_ip_info的Javascript物件,
var remote_ip_info = {
"ret":1,
"start":-1,
"end":-1,
"country":"\u4e2d\u56fd",
"province":"\u6cb3\u5357",
"city":"\u8bb8\u660c",
"district":"",
"isp":"",
"type":"",
"desc":""
};
那麼在本地的測試頁面中可以使用這個介面獲取所在的城市,程式碼如下:
//獲取當前城市
function getCity(url) {
var city = '';
//建立一個script標籤
var script = document.createElement("script");
//設定script標籤引用的url
script.setAttribute("src",url);
//將script標籤新增到頭部,也可以新增到其他地方
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
//如果script標籤載入完成,獲取城市
script.onload = function() {//script.onload不相容IE8及以下版本
city = remote_ip_info["city"];
console.log(city);
}; //移除建立的script標籤
head.removeChild(script);
} //該URL是新浪的獲取當前城市的API,採用jasop方法獲取資料
getCity("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js");
下面是聚合資料的天氣預報介面。
介面示例:http://op.juhe.cn/onebox/weather/query?cityname=%E6%B8%A9%E5%B7%9E&key=您申請的KEY
使用這個介面時至少要傳入三個引數:
- cityname是要查詢的城市名,可以用前面新浪的城市查詢介面得到的資料。
- key是申請使用聚合資料天氣預報介面時,給的APPKey。
- 另外還有一個callback引數,傳入一個函式名,伺服器會把查詢後得到的資料拼接成函式呼叫的形式返回來,比如傳入test函式名,則返回來test({"date":"2011-11-11","wind":"微風"}),所以要在測試頁面宣告好這個函式,以便對返回來的資料進行處理。
下面是程式碼:
html程式碼,將獲得資料在頁面中顯示出來
<p id="chuanyi"></p>
<p id="yundong"></p>
<p id="ziwaixian"></p>
獲得天氣情況資料的程式碼,原理與獲取當前城市的原理一樣,不同的是要事先指定回撥函式,並將函式名加到url的末尾。
function $(id) {
return document.getElementById(id);
} //指定回撥函式
function callback(data) {
$("chuanyi").innerHTML = "穿衣指數:" + data.result.data.life.info.chuanyi;
$("yundong").innerHTML = "運動指數:" + data.result.data.life.info.yundong;
$("ziwaixian").innerHTML = "紫外線:" + data.result.data.life.info.ziwaixian;
} function getWeather(city) {
var url = 'http://op.juhe.cn/onebox/weather/query?cityname='+encodeURIComponent(city)+'&key=abbac0fc4fccbec34891de77bb1cfb4e&callback=callback';//該URL是聚合資料的天氣預報API,採用jasop方法獲取資料
console.log(encodeURIComponent(city));
var script = document.createElement("script");
script.setAttribute("src",url);
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
head.removeChild(script);
}
有了以上程式碼,還不能獲取到天氣資料,因為getWeather()函式還沒有呼叫。為了能將獲取到的城市資料傳給getWeather()函式,要在getCity()函式內部script的onload事件處理程式中呼叫getWeather()函式。
下面是呼叫getWeather函式返回的資料,一大串:
callback({
"reason":"successed!",
"result":{
"data":{
"pubdate":"2016-11-11",
"pubtime":"11:00:00",
"realtime":{
"city_code":"101180401",
"city_name":"許昌",
"date":"2016-11-11",
"time":"16:00:00",
"week":5,
"moon":"十月十二",
"dataUptime":1478853365,
"weather":{
"temperature":"15",
"humidity":"64",
"info":"多雲",
"img":"1"
},
"wind":{
"direct":"南風",
"power":"3級",
"offset":null,
"windspeed":null
}
},
"life":{
"date":"2016-11-11",
"info":{
"chuanyi":["較冷","建議著厚外套加毛衣等服裝。年老體弱者宜著大衣、呢外套加羊毛衫。"],
"ganmao":["較易發","天涼,晝夜溫差較大,較易發生感冒,請適當增減衣服,體質較弱的朋友請注意適當防護。"],
"kongtiao":["較少開啟","您將感到很舒適,一般不需要開啟空調。"],
"xiche":["較適宜","較適宜洗車,未來一天無雨,風力較小,擦洗一新的汽車至少能保持一天。"],
"yundong":["較適宜","天氣較好,但考慮氣溫較低,推薦您進行室內運動,若戶外適當增減衣物並注意防晒。"],
"ziwaixian":["弱","紫外線強度較弱,建議出門前塗擦SPF在12-15之間、PA+的防晒護膚品。"]
}
},
"weather":[
{
"date":"2016-11-11",
"info":{
"day":["1","多雲","17","","微風","06:53"],
"night":["0","晴","7","","微風","17:23"]
},
"week":"五",
"nongli":"十月十二"
},
{
"date":"2016-11-12",
"info":{
"dawn":["0","晴","7","無持續風向","微風","17:23"],
"day":["0","晴","17","","微風","06:54"],
"night":["1","多雲","8","","微風","17:23"]
},
"week":"六",
"nongli":"十月十三"
},
{
"date":"2016-11-13",
"info":{
"dawn":["1","多雲","8","無持續風向","微風","17:23"],
"day":["1","多雲","17","","微風","06:55"],
"night":["0","晴","9","","微風","17:22"]
},
"week":"日",
"nongli":"十月十四"
},
{
"date":"2016-11-14",
"info":{
"dawn":["0","晴","9","無持續風向","微風","17:22"],
"day":["1","多雲","17","","微風","06:56"],
"night":["2","陰","5","","微風","17:21"]
},
"week":"一",
"nongli":"十月十五"
},
{
"date":"2016-11-15",
"info":{
"dawn":["2","陰","5","無持續風向","微風","17:21"],
"day":["1","多雲","13","","微風","06:57"],
"night":["1","多雲","6","","微風","17:21"]
},
"week":"二",
"nongli":"十月十六"
}
],
"f3h":{
"temperature":[
{
"jg":"20161111170000",
"jb":"15"
},
{
"jg":"20161111200000",
"jb":"13"
},
{
"jg":"20161111230000",
"jb":"10"
},
{
"jg":"20161112020000",
"jb":"10"
},
{
"jg":"20161112050000",
"jb":"8"
},
{
"jg":"20161112080000",
"jb":"7"
},
{
"jg":"20161112110000",
"jb":"14"
},
{
"jg":"20161112140000",
"jb":"16"
},
{
"jg":"20161112170000",
"jb":"14"
}
],
"precipitation":[
{"jg":"20161111170000","jf":"0"},
{"jg":"20161111200000","jf":"0"},
{"jg":"20161111230000","jf":"0"},
{"jg":"20161112020000","jf":"0"},
{"jg":"20161112050000","jf":"0"},
{"jg":"20161112080000","jf":"0"},
{"jg":"20161112110000","jf":"0"},
{"jg":"20161112140000","jf":"0"},
{"jg":"20161112170000","jf":"0"}
]
},
"pm25":{
"key":"Xuchang",
"show_desc":0,
"pm25":{
"curPm":"50",
"pm25":"25",
"pm10":"51",
"level":1,
"quality":"優",
"des":"可正常活動。"
},
"dateTime":"2016年11月11日16時",
"cityName":"許昌"
},
"jingqu":"",
"jingqutq":"",
"date":"",
"isForeign":"0"
}
},
"error_code":0
})
這篇文章講解的很詳細:jsonp協議原理深度解析
(完)