第一次發文章,有點小緊張。es6
什麼是同源策略?json
爲何要有跨域限制?後端
爲何script
標籤能跨域?跨域
這些基本的概念想必不用過多說明了,咱們切入正題:bash
在開發項目中若是有用到jsonp
,一直使用的是一個jsonp
包。使用過程當中參數須要格式化,也不曾進行Promise
化,須要本身封裝。因而想一想能不能在包內就把這些事給辦了,之後調用就省心不少,因而點開jsonp
包的源碼研讀一番,這裏簡單說明下jsonp
的實現原理,簡單來講就是這樣的:app
1. 客戶端須要在本身這邊有一個定義好的接收服務端數據的函數函數
2. 使用script標籤發起一個服務端地址的請求jsonp
3. 服務端響應,對應的函數執行,傳參完成ui
這是最簡單的jsonp
實現,這裏須要寫一個函數,須要寫一個script
標籤,好像一點都不cool
~url
接下來咱們手寫一個本身的jsonp
,固然了,服務端怎麼知道你本地的準備函數是什麼,須要客戶端告訴它,並且服務端會根據客戶端提供的其餘參數動態生成數據給到咱們。因此咱們主要是實現如下幾個點:
1. 調用時動態建立script標籤
2. 註冊一個全局的方法等待被執行
3. 等待函數必定要在標籤發起請求以前準備好
4. 獲得數據以後移除建立的script標籤
5. 函數名不能重名,可能會同時發起多個請求
從上所述,在這裏咱們的方法須要如下幾個參數:
. url: 後端地址
. prefix: 執行函數名的前綴,後綴使用自增來確保惟一性
. param: 與後端協商好的發起jsonp請求時的字段
. timeout: 請求超時時間
. data: 服務端須要的其餘參數
jsonp
實現跨域請求也有本身的侷限,如只能發起get
請求。咱們這裏再原有的包上進行必定功能拓展,使用es6
重構並將它Promise
化,重構以後的代碼jsonp
以下:
// 其餘參數在opts內
function jsonp(url, opts) {
// 實現Promise化
return new Promise((resolve, reject) => {
// 自增值初始化
let count = 0;
//設置默認參數
const {
prefix = '__jp',
param = 'callback',
timeout = 60000,
data = {}
} = opts;
let name = prefix + count++;
let timer;
//清除script標籤以及註冊的全局函數以及超時定時器
function cleanup() { // 清除函數
if (script.parentNode) {
script.parentNode.removeChild(script);
window[name] = null;
if (timer) {
clearTimeout(timer);
}
}
}
if (timeout) { // 超時
timer = setTimeout(() => {
cleanup();
reject('timeout');
}, timeout);
}
// 註冊全局函數,等待執行中...
window[name] = res => {
// 只要這個函數一執行,就表示請求成功,可使用清除函數了
if (window[name]) {
cleanup();
}
// 將請求到的數據扔給then
resolve(res);
}
// 如下將data對象格式的參數拼接到url的後面
let str = '';
for (const key in data) {
const value = data[key] !== undefined ? data[key] : '';
str += `&${key}=${encodeURIComponent(value)}`;
}
url = url + (url.indexOf('?') > 0 ? '' : '?') + str.substr(1);
// 最後加上與服務端協商的jsonp請求字段
url = `${url}&${param}=${name}`;
const script = document.createElement('script');
script.src = url;
// 如下這條執行且成功後,全局等待函數就會被執行
document.head.appendChild(script);
})
}
複製代碼
# 大功告成,咱們請求一個QQ音樂的輪播圖數據試下:
const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg';
const opts = {
data: {
g_tk: 1928093487,
inCharset: 'utf-8',
outCharset: 'utf-8',
notice: 0,
format: 'jsonp',
platform: 'h5',
uin: 0,
needNewCode: 1
},
// QQ音樂接口Jsonp字段
param: 'jsonpCallback'
}
jsonp(url, opts)
.then(res => {
console.log(res);
})
.catch(ex => {
console.log(ex);
})
複製代碼
數據請求成功!