封裝-Jsonp
- Jsonp
-
- jsonp是什麼?
- 為什麼使用jsonp?
- 原理---jsonp的通俗講解
-
- 案例---使用script的src擷取跨域資料
- 封裝Jsonp
-
- 案例---擷取qq.com中的城市資訊與天氣資訊
Jsonp
jsonp是什麼?
- 通過script标簽中的src屬性沒有同源限制的方法進行擷取資料
即通過script标簽來跨域擷取資料,名為JSON with Padding,
或者就叫JSONP。JSONP的原理很簡單,但需要伺服器端給予相應配合。
大緻來說,JSONP的實作思路就是在用戶端程式設計時作好使用JSON資料的準備,然後再通過圓括号将這些資料括起來以建立一條有效的JavaScript語句(可能是一次有效的函數調用)。
也就是說,用戶端可以使用一個用于命名jsonp的查詢參數來決定可以擷取的資料。最簡單的情況下,如果jsonp參數為空,則傳回的資料就是被括在圓括号中的JSON
為什麼使用jsonp?
浏覽器安全模型規定,XMLHttpRequest、架構(frame)等隻能在一個域中通信。
從安全角度考慮,這個規定很合理;
但是,也确實給分布式(面向服務、混搭等等本周提到的概念)Web開發帶來了麻煩。
-
本地代理:
需要一些硬體設施(沒有伺服器的用戶端無法運作),并且帶寬和潛伏時間也要加倍(遠端伺服器-代理伺服器-用戶端)。
-
Flash:
遠端主機中需要部署一個crossdomain.xml檔案,而且,Flash作為一門專有技術,其前途尚不明朗;換句話說,開發人員很可能要學習一種目标不确定的程式設計語言。
-
Script标簽:
無法确切知道内容是否有效,沒有标準的實作方法,又可能被認為是一種“安全風險”。
原理—jsonp的通俗講解
- 使用script 的src屬性沒有同源限制,跨域名擷取資料的一種方法
案例—使用script的src擷取跨域資料
<script type="text/javascript">
// 定義一個jp函數
function jp(data) {
console.log(data);
}
</script>
<!-- 使用script 的src屬性沒有同源限制,跨域名擷取資料的一種方法 -->
<!-- 擷取qq.com的天氣資訊,callback是傳回的資料,jp是傳回資料後調用的方法 -->
<script src="https://apis.map.qq.com/ws/location/v1/ip?callback=jp&key=CAABZ-AVSAQ-RDR5L-GTBDJ-HLA4O-A5FDB&output=jsonp" ></script>
封裝Jsonp
- 原理:
- 使用script 的src屬性沒有同源限制,跨域名擷取資料的一種方法
- 動态的建立script标簽,使用src屬性來進行跨域擷取資料
- 其中使用有Promise,異步
// jsonp 利用script的src屬性沒有同源限制,跨域名擷取資料的一種方法
// 後端傳回的資料格式是,方法名(資料)
// url 最重要 callback名稱 jp 是callback的值
function jsonp(url,option={}) {
// 預設回調函數參數名 callback
var jp = option.jp||"callback";
// 預設回調函數參數值
var callback = option.callback||"jp";
return new Promise((resolve,reject) => {
// 檢視url是否有jp 沒有還要加上
if(url.indexOf(jp)==-1){
url += "&"+jp+"="+callback;
}
// 如果有擷取這已經加上了callback=值,擷取callback的值
var p1 = url.indexOf(option.jp); // 擷取到callback的位置
var p2 = url.indexOf("&",p1); // 從p1的位置開始查找符号"&"
// 如果查找不到設定的p2位置,
p2 == -1 ? p2 = url.lenght;
// 就在url的末尾加,
callback = url.slice(p1+jp.length+1,p2);
// 動态的建立callback方法
window[callback]=function(data){
// 删除
document.head.removeChild(script);
resolve(data);
}
// 動态建立script标簽
let script = document.createElement("script");
// 将需要擷取的資料的位址指派給script 中的src屬性,跳過浏覽器的同源政策
script.src = url;
// 将script标簽追加到head中
document.head.append(script);
// script加載失敗
script.onerror = function(e){
// 動态删除script
document.head.removeChild(script);
reject(e)}
})
}
案例—擷取qq.com中的城市資訊與天氣資訊
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p id="city" ></p>
<img src="" id="img" alt="">
<p id="weather"></p>
</body>
<script type="text/javascript">
// jsonp 利用script的src屬性沒有同源限制,跨域名擷取資料的一種方法
// 後端傳回的資料格式是,方法名(資料)
// url 最重要 callback名稱 jp 是callback的值
function jsonp(url,option={}) {
// 預設回調函數參數名 callback
var jp = option.jp||"callback";
// 預設回調函數參數值
var callback = option.callback||"jp";
return new Promise((resolve,reject) => {
// 檢視url是否有jp 沒有還要加上
if(url.indexOf(jp)==-1){
url += "&"+jp+"="+callback;
}
// 如果有擷取這已經加上了callback=值,擷取callback的值
var p1 = url.indexOf(jp); // 擷取到callback的位置
var p2 = url.indexOf("&",p1); // 從p1的位置開始查找符号"&"
// 如果查找不到設定的p2位置,
p2 == -1 ? p2 = url.lenght : '';
// 就在url的末尾加,
callback = url.slice(p1+jp.length+1,p2);
// 動态的建立callback方法
window[callback]=function(data){
// 删除
document.head.removeChild(script);
resolve(data);
}
// 動态建立script标簽
let script = document.createElement("script");
// 将需要擷取的資料的位址指派給script 中的src屬性,跳過浏覽器的同源政策
script.src = url;
// 将script标簽追加到head中
document.head.append(script);
// script加載失敗
script.onerror = function(e){
// 動态删除script
document.head.removeChild(script);
reject(e)
}
})
}
var url1 = "https://apis.map.qq.com/ws/location/v1/ip?key=CAABZ-AVSAQ-RDR5L-GTBDJ-HLA4O-A5FDB&output=jsonp";
var url2 = "https://wis.qq.com/weather/common?weather_type=observe|forecast_24h|air&source=pc&callback=jp";
// 擷取位址
jsonp(url1)
.then(res=>{
url2 = url2+`&province=${res.result.ad_info.province}&city=${res.result.ad_info.city}`;
city.innerHTML=res.result.ad_info.city;
return jsonp(url2); //擷取天氣
})
.then(res=>{
weather.innerHTML=res.data.observe.degree+"℃";
// 動态的天氣圖檔
img.src="https://mat1.gtimg.com/pingjs/ext2020/qqindex2018/dist/img/weather/"+res.data.observe.weather_code+".svg";
})
</script>
</html>