jsonp 欄目 JSON 简体版
原文   原文鏈接

數據庫

有時候,咱們須要長久的存儲數據,不隨着外界因素的改變而改變,這樣的數據棲息地能夠稱之爲數據庫。javascript

version1.0-表面工做

創建一個扣款功能作例子

jsonp_扣款

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">100</span></p>
    <button id='button'>付款1塊</button>
</body>
<script>
    $('#button').on('click',() => {
        let n=document.getElementById('amount').innerText;
        n -= 1;
        document.getElementById('amount').innerText = n;
    })
</script>
</html>

存在的問題

之因此稱之爲表面,是由於,全部的操做都是表面現象,當頁面刷新時,一切都會回到初始狀態。並且,別人很容易就可以獲取你的信息。最好是,咱們去調取數據庫裏你的信息,而後顯示在頁面上。html

version2.0-將操做在後端進行

以往,咱們是在前端的代碼中去改用戶的餘額。如今,不,咱們去後端直接改數據庫裏的餘額,而後,把後端數據庫的金額顯示在前端頁面上。而前端用來向後端發起一個請求,請求更改數據庫裏的餘額。前端

<button>顯然不能發起一個請求,因此使用<form>,表單是能夠發起一個請求的,並且能夠指定請求的方式:get/postjava

前端代碼:jquery

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <form id='button' action='/pay' method='post'>
        <input type='submit' value='付款1塊錢'>
    </form>
</body>
</html>

使用Javascript寫的後端代碼:ajax

//讀取數據庫金額,並顯示在頁面上
if(path === './'){
    var index_string = fs.readFileSync('./index.html','utf8');
    var amount = fs.readFileSync('./db','utf8');
    index_string.replace('$$amount$$',amount);
    response.setHeader('Content-type','text/html;charset=utf-8');
    response.write(string);
    response.end();
    
//修改數據庫中金額,並返回success
}else if(path === '/pay' && method === 'post'){
    var amount = fs.readFileSync('./db','utf8');
    var newAmount = amount - 1;
    fs.writeFileSync('./db',newAmount);
    response.write('success');
    response.end();
}

效果

點擊按鈕:數據庫

點擊付款1塊錢,發送以下請求,而且頁面中顯示success。說明請求成功,數據庫中金額修改爲功!
form_post請求json

返回頁面:
jsonp_2後端

查看數據庫:
jsonp_2_數據庫服務器

存在問題

有沒有發現,咱們每一次付款,都須要返回、刷新頁面才能看到本身正確的餘額。用戶體驗極差!
而且,<form>表單自帶特性:每一次提交,都會刷新頁面。
最好是,付款以後,頁面只有數字那部分局部刷新

version3.1-iframe

<iframe>的功能就是在頁面中單開一個空間做爲新的頁面,咱們能夠將<form>關聯到這個<iframe>上,每次提交都只刷新<iframe>便可。

<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <form id='button' action='/pay' method='post' target='result'>
        <input type='submit' value='付款1塊錢'>
    </form>

    <iframe name='result' src='about:blank' frameborder='0' height=200></iframe>
</body>

jsonp_iframe

存在的問題

過期。雖然不須要再按返回了,可是仍是須要刷新頁面才能看到真正的餘額。

version3.2-<img>發送請求

既然,咱們不但願頁面刷新,那咱們就得換一個方式發送請求。鐺鐺鐺鐺!這就是<img>!不過<img>只能發送get請求,不過,方法先進,用戶體驗良好,get也能夠接受。

前端代碼:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <button id='button'>付款1塊錢</button>>
</body>
<script>
    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('img');
        image.src='./pay';
        image.onload=function(){
            alert('success');
            amount.innerText -= 1;
        }
        image.onerror=function(){
            alert('fail');
        }
    })
</script>

使用Javascript寫的後端代碼:

//和以前同樣,讀取數據庫中金額,並顯示在頁面上,但這裏並不必定會用到這些代碼,由於咱們不須要刷新頁面了
if(path === './'){
    var index_string = fs.readFileSync('./index.html','utf8');
    var amount = fs.readFileSync('./db','utf8');
    index_string.replace('$$amount$$',amount);
    response.setHeader('Content-type','text/html;charset=utf-8');
    response.write(string);
    response.end();
}

//修改數據庫中金額,成功後返回一張圖片
else if(path === '/pay'){
    var amount = fs.readFileSync('./db','utf8');
    var newAmount = amount - 1;
    fs.writeFileSync('./db',newAmount);
    response.setHeader('Content-type','image/jpg');
    response.write(fs.readFileSync(你的圖片路徑));
    response.end();
}

jsonp_img

優勢

問題

哇!這樣已經很好了,能夠返回一個大小很小的圖片呀,或者什麼亂起八糟的圖片。可是,圖片畢竟是圖片,返回一張圖片會影響響應速度。

version3.3-<script>發送請求

咱們發現,<script>竟然也能發送請求,那更好了,<script>請求返回的是空字符串,那可比圖片小多了。

前端代碼:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <button id='button'>付款1塊錢</button>>
</body>
<script>
    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('script');
        script.src='./pay';
        document.body.appendChild(script);
        script.onload=function(){
            alert('success');
        }
        script.onerror=function(){
            alert('fail');
        }
    })
</script>

使用Javascript寫的後端代碼:

//和<img>發送請求時的沒有區別
if(path === './'){
    var index_string = fs.readFileSync('./index.html','utf8');
    var amount = fs.readFileSync('./db','utf8');
    index_string.replace('$$amount$$',amount);
    response.setHeader('Content-type','text/html;charset=utf-8');
    response.write(string);
    response.end();
    
//修改數據庫中金額,成功後返回空字符串
}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.write('');
    response.end();
}

jsonp_script

優化-1

在使用<script>發送請求的本例中,咱們沒有在前端代碼中加入,當script.onload=function(){}時,頁面中數字減一amount.innerText -= 1;。這是由於,通過測試,

在前端代碼中加入:

script.onload=function(){
    //以前的代碼
    console.log('我是前端');
}

在後端代碼中加入:

else if(path === '/pay'){
    //以前的代碼
    response.write('console.log("我是後端")');
    //以後的代碼
}

結果發現,首先出現後端的console.log("我是後端"),再出現前端的console.log('我是前端')

以前,咱們將amount.innerText -= 1;放在前端代碼裏是由於,以前使用<img>,返回只能是一張圖片;如今咱們使用<script>,返回值會被當作Javascript執行。

因此,咱們直接在後端代碼輸入咱們想要執行的前端代碼就行啦👍👍👍

改進後的後端代碼:

<Script>
    if(path === './'){
        var index_string = fs.readFileSync('./index.html','utf8');
        var amount = fs.readFileSync('./db','utf8');
        index_string.replace('$$amount$$',amount);
        response.setHeader('Content-type','text/html;charset=utf-8');
        response.write(string);
        response.end();
    }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.write(`
            // console.log("我是後端");
            amount.innerText -= 1;
            // alert("success");
        `);
        response.end();
    }
</Script>

jsonp_script_2

優化-2

只有一個問題了(๑•̀ㅂ•́)و✧,使用<script>發送請求會在頁面中實際的添加<script></script>標籤才能生效,因此,點多少次按鈕就會添加多少個<script></script>標籤。
jsonp_script_3

修改後的前端代碼:

<script>
    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('script');
        script.src='./pay';
        document.body.appendChild(script);
        script.onload=function(e){
            // alert('success');
            e.currentTarget.remove();
        }
        script.onerror=function(){
            // alert('fail');
            e.currentTarget.remove();
        }
    })
</script>

不錯👍👍👍

爲何要在前端代碼裏優化這個問題呢??由於先執行的是後端代碼,最後執行的是前端代碼,功能是先在後端實現的,因此,在前端裏刪除這個標籤也不會對功能產生什麼影響。

小結

前端代碼:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <button id='button'>付款1塊錢</button>>
</body>
<script>
    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('script');
        script.src='./pay';
        document.body.appendChild(script);
        script.onload=function(e){
            // alert('success');
            e.currentTarget.remove();
        }
        script.onerror=function(){
            // alert('fail');
            e.currentTarget.remove();
        }
    })
</script>

使用Javascript寫的後端代碼:

if(path === './'){
    var index_string = fs.readFileSync('./index.html','utf8');
    var amount = fs.readFileSync('./db','utf8');
    index_string.replace('$$amount$$',amount);
    response.setHeader('Content-type','text/html;charset=utf-8');
    response.write(string);
    response.end();
}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.write(`
        // console.log("我是後端");
        amount.innerText -= 1;
        // alert("success");
    `);
    response.end();
}

該方法稱之爲SRJ:Server Rendered Javascript,即後端返回Javascript

先後端職能分離

在功能上,上述代碼很是好的實現了前端與後端交互,調用數據庫數據顯示在頁面上。可是,在職能上,先後端仍是有耦合的現象,在這裏,後端裏寫進了前端代碼amount.innerText -= 1;。因此,此時,咱們須要解耦

解耦的思路很簡單,將服務器上的前端代碼都放到前端的一個函數裏,而後,後端只須要調用這個函數就OK了。

使用Javascript寫的後端代碼:
將下面這段:

if(path === './'){
    //your code
}else if(path === '/pay'){
    //your code
    response.write(`
        // console.log("我是後端");
        amount.innerText -= 1;
        // alert("success");
    `);
    response.end();
}

修改成:

if(path === './'){
    //your code
}else if(path === '/pay'){
    //your code
    response.write(`
        ${query.callbackName}.call(undefined,'success');      //👈👈新添加
    `);
    response.end();
}

前端代碼:
<script>添加函數,而且,在<script>裏添加請求參數?callbackName=xxx

<!DOCTYPE html>
<!--your code-->
<script>
    window.yyy=function(status){                        //👈👈新添加
        if(status === 'success'){
            amount.innerText -= 1;
        }
        else if(){}
    }

    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('script');
        script.src='./pay?callbackName=yyy';            //👈👈新添加
        document.body.appendChild(script);
        script.onload=function(e){
            // alert('success');
            e.currentTarget.remove();
        }
        script.onerror=function(){
            // alert('fail');
            e.currentTarget.remove();
        }
    })
</script>

解釋:
如今,前端裏新添加的<script>的地址裏傳入了參數?callbackName=yyy,而且添加了回調函數window.yyy=function(){}。後端裏,使用${query.callbackName}來查找地址裏?callbackName的所指,而後使用call()來調用所指的yyy函數。

效果:
jsonp先後端分離

JSONP

構造

看下後端代碼:

if(path === './'){
    //your code
}else if(path === '/pay'){
    //your code
    response.write(`
        ${query.callbackName}.call(undefined,'success');      //👈👈注意!!!
    `);
    response.end();
}

看到👈👈注意!!!的那一行,咱們回調時傳入的參數是'success',是個字符串,字符串前面的內容咱們稱之爲左Padding,後面的內容咱們稱之爲右Padding。當傳入的參數是個JSON的時候,咱們就稱之爲JSON+PaddingJSONP

if(path === './'){
    //your code
}else if(path === '/pay'){
    //your code
    response.write(`
        ${query.callbackName}.call(undefined,{     //👈👈注意!!!
            "success":true;
            "left":${newAmount};
        });      
    `);
    response.end();
}

引伸義,那麼像這樣前端和後端交互,後端又傳入一個參數回調一個函數的技術,叫作JSONP。

邏輯和約定

JSONP流程和約定

根據約定的修改代碼

前端代碼:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>pay</title>
</head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<body>
    <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p>
    <button id='button'>付款1塊錢</button>>
</body>
<script>
    $('#button').on('click',function(){
        var amount=document.getElementById('amount');
        var image=document.createElement('script');
        
        var functionName="philip"+parseInt(Math.random()*100000,10);    👈👈//隨機函數名
        script.src='./pay?callback='+functionName;                 👈👈//傳入隨機函數名
        window[functionName]=function(status){                👈👈//隨機函數名的函數
            if(status === 'success'){
                amount.innerText -= 1;
            }else if(){}
        }
        
        document.body.appendChild(script);
        script.onload=function(e){
            // alert('success');
            e.currentTarget.remove();
            delete window[functionName];
        }
        script.onerror=function(){
            // alert('fail');
            e.currentTarget.remove();
            delete window[functionName];
        }
    })
</script>

後端代碼:

if(path === './'){
    var index_string = fs.readFileSync('./index.html','utf8');
    var amount = fs.readFileSync('./db','utf8');
    index_string.replace('$$amount$$',amount);
    response.setHeader('Content-type','text/html;charset=utf-8');
    response.write(string);
    response.end();
}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.write(`
        ${query.callback}.call(undefined,'success');         👈👈//修改成${query.callback}
    `);
    response.end();
}

使用jQuery

$.ajax({
    url: "",                          👈👈//要請求的網址
    dataType: "jsonp",                👈👈//我要使用jsonp
    success: function(status){        👈👈//響應成功後,我要執行的函數
        if(status === 'success'){
            amount.innerText -= 1;
        }
    }
})

jQuery幫咱們發送請求時傳入符合行業約定的參數:
jsonp_jquery_1
jQuery幫咱們生成隨機函數名的回調函數:
jsonp_jquery_2

Q&A

Q:爲何JSONP不能使用POST請求?
A:一、JSONP是經過動態建立script實現的;二、動態建立script不能發送get請求

總結

一、使用<form>會刷新頁面
二、改用<img>發送請求,但只能知道成功與否,沒有更多信息
三、改用<script>
四、如何獲得更多內容,添加?callback參數
五、後端傳入一個參數,調用下callback函數
六、OK,獲得內容,最終的方法三、四、5,就是JSONP

相關文章
相關標籤/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
本站公眾號
   歡迎關注本站公眾號,獲取更多信息