介紹JSONP之前 ,我們先了解一下,頁面數據交互的發展過程。來看下面一個例子。
付款是我們日常中常見的一種金錢交易,用戶在頁面中點擊付款按鈕,網頁提交請求給服務器,服務器收到請求后在數據庫對金額進行扣減,然后將消息返回給頁面,告訴用戶給付款成功。
在jsonp ajax的概念實現之前,以上付款的網頁數據交互過程是怎么實現的呢?
大家都知道form表單有提交post請求功能,我們可以利用form表單,告訴服務器【請求扣款】
頁面代碼如下:用表單提交post請求,請求路徑為/pay
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h5>您的賬戶余額是<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post">//用表單提交post請求,請求路徑為/pay
<input type="submit" value="付款">
</form>
</body>
</html>
服務器代碼:
當請求的路徑為/pay時,數據庫進行扣減金額一塊錢,然后給頁面返回success,否則告訴頁面請求出錯了。
else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8');
var newAmount = amount - 1;
fs.writeFileSync('./db',newAmount);
response.write('success');
response.end();
}
else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('噢噢,好像出錯了呢')
response.end()
}
效果如下:
這時候大家會發現一個問題,每次頁面返回的success后都要再次刷新頁面才能看到賬戶余額,這樣的體驗并不好,這時候神奇的前端程序員們又想到了一個辦法SRJ方案 SRJ - Server Rendered JavaScript。
這時候就不得不佩服前輩們的‘腦洞’啦。程序員想到script標簽也可以發送請求,因此可以通過動態的創建script標簽來向服務器發送請求,服務器完成操作后,返回結果動態的修該頁面的賬戶金額;同時本地頁面監聽script標簽的onload,onerror事件,來刪除動態創建的script標簽,節省內存。
服務器代碼實現如下:
else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8');
var newAmount = amount - 1;
fs.writeFileSync('./db',newAmount);
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200;
response.write(`amount.innerText = amount.innerText - 1`);
response.end();
}
else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('噢噢,好像出錯了呢')
response.end()
}
頁面實現:
<h5>您的賬戶余額是<span id="amount">&&&amount&&&</span></h5>
<button id="button">打錢</button>
<script>
button.addEventListener('click',function(e){
let script = document.createElement('script');
script.src = '/pay';
document.body.appendChild(script);
script.onload = function(e){
e.currentTarget.remove();
}
script.onerror = function (e) {
alert('fail');
e.currentTarget.remove();
}
})
</script>
解決了同一網站的信息交互,那么不同的網站之間(不同的域名),數據要怎么進行交互呢?以上的SRJ也是可以行得通的,只要修改地址為對方網站地址即可。因為script標簽的請求不受域名限制。但是我們發現,如果使用SRJ方案,雙方網站的代碼耦合度太高,為了代碼的解耦,有人提出了JSONP方案:
JSONP(JSON with Padding)是JSON的一種“使用模式”,可用于解決主流瀏覽器的跨域數據訪問的問題。
JSONP的主要思路是:
1.請求方通過動態創建的script標簽的src路徑指向響應方,并且在路徑上添加一個查詢參數(按照約定,參數名為callback),該查詢參數告知響應方應該調用的函數名。同時請求方在本地定義了這個調用函數。如下:
let functionName = 'tina'+parseInt(Math.random()*100000,10);
//給調用函數創建一個隨機的函數名
script.src = 'http://u.com:8002/pay?callback=' + functionName;
//u.com為響應網站的域名,8002為響應網站的端口號
window[callback] = function(result){ //定義函數
if(result === 'success'){
amount.innerText = amount.innerText - 1;
}
}
script.onload = function(e){
e.currentTarget.remove();//執行結束刪除script標簽
delete window[callback]; //執行結束刪除函數
}
script.onerror = function (e) {
alert('fail');
e.currentTarget.remove();//執行結束刪除script標簽
delete window[callback];//執行結束刪除函數
}
})
2.響應方根據查詢參數查找到函數名后,回調這個函數,做出響應。當回調函數響應的數據('success'P)是json格式時以下的代碼就是JSONP。
response.write(`${query.callback}.call(undefinded,'success')`);
3.完成以上步驟后,請求方得到了響應的數據,就會返回到頁面上。
以上代碼是原生JS的實現,JQuery也封裝了JSONP,代碼如下
//u.com為響應網站的域名,8002為響應網站的端口號
$.ajax({
url: "http://u.com:8002/pay",
dataType: "jsonp",
success: function( response ) {
if(response === 'success'){
amount.innerText = amount.innerText - 1
}
}
})
$.jsonp()
這里值得注意一下JQuery封裝的JSONP雖然起了一個與ajax相近的名字,但是與ajax沒有關系,因為ajax不支持跨域請求。
了解完以上對JSONP的解釋,我們也能很容易明白JSONP為什么不支持POST請求,是因為JSONP是通過動態創建scipt實現的,并且script標簽不支持POST請求。
以上是我個人對JSONP的理解,如果錯誤歡迎指出。