天天看點

封裝-JsonpJsonp

封裝-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>

           

繼續閱讀