jsonp是一個經(jīng)典的跨域解決方案,雖然現(xiàn)在使用的很少,但是這種經(jīng)典的解決方案還是應(yīng)該了解下,最近在看github中開源的jsonp庫,跟著源碼加上查閱資料了解了一下jsonp前后端主要的工作,借此來分享一下,有不對的地方大家可以留言指出。
前端需要傳遞的內(nèi)容
訪問的服務(wù)器方法名
返回json前端調(diào)用方法
服務(wù)器需要完成的內(nèi)容
按照前端給的回調(diào)函數(shù)名包裹成字符串返回
解析github的jsonp項目
傳遞參數(shù)
使用這個庫需要傳遞三個參數(shù)
- url
訪問的服務(wù)器函數(shù)
-
opt
1. name:本地接受服務(wù)器返回json的函數(shù)名稱的
2. param、preffix 服務(wù)器解析請求并返回json的函數(shù)名稱與接受服務(wù)器返回json的函數(shù)名稱前綴
3. timeout 等待服務(wù)器返回結(jié)果的最長時間
fn
你希望執(zhí)行的回調(diào)函數(shù)
分析jsonp庫代碼
發(fā)起jsonp請求
var target = document.getElementsByTagName('script')[0] || document.head;
script = document.createElement('script');
script.src = url;
target.parentNode.insertBefore(script, target);
代碼很簡單就是在本地創(chuàng)建script然后發(fā)起url請求
處理等待最大時間方法
if (timeout) {
timer = setTimeout(function(){
cleanup();
if (fn) fn(new Error('Timeout'));
}, timeout);
}
jsonp庫在發(fā)起請求前首先執(zhí)行這部分代碼,當(dāng)在給定的時間內(nèi)這個定時器沒有清楚就會執(zhí)行清理函數(shù),這里這里值得注意的是,如果有回調(diào)函數(shù)也會給回調(diào)函數(shù)拋一個錯誤。
清理函數(shù)
寫通用工具方法,要記得寫清理方法處理不再使用的元素,避免浪費資源
function cleanup(){
if (script.parentNode) script.parentNode.removeChild(script);
window[id] = noop; // 將處理服務(wù)器返回數(shù)據(jù)函數(shù)賦值為空函數(shù)
if (timer) clearTimeout(timer);
}
首先清理了發(fā)起jsonp請求的script標簽
接著清理監(jiān)聽等待函數(shù)返回最大時間的定時器
并將本地監(jiān)聽函數(shù)設(shè)置為空函數(shù)。
監(jiān)聽服務(wù)器返回
window[id] = function(data){
cleanup(); // 不要忘記清理
if (fn) fn(null, data);
};
在window對象上監(jiān)聽遠端返回,這個id就是前邊設(shè)置的參數(shù)組裝而成的,這里要注意一下這里不能完了執(zhí)行清理函數(shù),以防資源浪費
服務(wù)器處理部分
const pathName = url.parse(req.url).pathname; // 獲取訪問服務(wù)器的函數(shù)名稱
const queryData = qs.parse(req.url.split('?')[1]); // 獲取請求參數(shù)部分
if (pathName === '/jsonp' && queryData['callback']) {
res.writeHead(200, {
'Content-Type': 'application/json;charset=utf-8' // 返回類型為json
});
var data = {
name: 'zhouyijun'
}
res.end(queryData.callback + `(${JSON.stringify(data)})`) // 返回值,要記得將json對象轉(zhuǎn)換為字符串
}
這里代碼比較簡單就是通過url解析參數(shù),分發(fā)給對應(yīng)的處理函數(shù),并且取出終端回調(diào)函數(shù)名稱使用字符串拼接成結(jié)果返回給終端。