用代碼理解--JSONP到底是什么鬼

html實(shí)現(xiàn)打錢效果

<h5>您的賬戶余額是 <span id="number">100</span></h5>
<button id="button">付款</button>
button.addEventListener("click", function(){
  number.innerText = number.innerText - 1
})

接下來用node來實(shí)現(xiàn)后臺(tái)打錢效果
創(chuàng)建index.js文件,下面代碼第二部分寫在里面,并且通過命令行node index 8888打開。

<form action="/pay" method="post">
  <input type="submit" value="付款">  
</form>

//index.js
if(path === '/pay' && method.toUpperCase() === 'POST'){
    var amount = fs.readFileSync('./db', 'utf8')
    var newAount = amount - 1
    if(Math.random() > 0.5){
      fs.writeFileSync('./db', newAount)
      response.statusCode = 200
      response.write("success")   
    } else{
      response.statusCode = 400
      response.write("fail")
    }
    response.end()  
  }

但是用form表單一旦提交了就一定會(huì)刷新當(dāng)前頁面。 這里用iframe,是了解前端的歷史。

下面用image圖片提交


button.addEventListener("click", function(e){
        let img = document.createElement("img")
        img.src = "/pay"
        img.onload = function(){
            alert("打錢成功")
        }
        img.onerror = function(){
            alert("打錢成功")
        }
    })

對(duì)應(yīng)的index.js里的類型要修改成圖片格式
//index.js
if(path === '/pay' ){
  var amount = fs.readFileSync('./db', 'utf-8')
  var newAmount = amount - 1
  if(Math.random()>0.5){
    fs.writeFileSync('./db',newAmount)
    response.setHeader('Content-Type', 'img/jpg')
    response.statusCode = 200
    response.write(fs.readFileSync('./dog.jpg'))
  }else{
    response.statusCode = 400
    response.write('fail')
  }
    response.end()
}

用圖片發(fā)請(qǐng)求挺厲害的,但是還有更厲害的:用script發(fā)請(qǐng)求。
script發(fā)請(qǐng)求,再建一個(gè)叫db的文件,后綴無所謂,在文件里寫個(gè)數(shù)據(jù) '100' (相當(dāng)于數(shù)據(jù)庫(kù))

<h5>賬戶余額是<span id="amount">&&&amount&&&</span></h5>

buttonss.addEventListener('click',(e)=>{
    let script = document.createElement('script')
    script.src = '/pay'
    script.onload = function(e){
      e.currentTarget.remove()
    }
    document.body.appendChild(script)
    script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
    }
})

//index.js
if(path === '/pay' ){
  var amount = fs.readFileSync('./db', 'utf-8')
  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()
}

這個(gè)方案叫做SRJ Server rendered javascript, 服務(wù)器返回的JavaScript 。

訪問另一個(gè)網(wǎng)站的script

用命令行添加兩個(gè)hosts sudo vi /etc/hosts(要權(quán)限),也可以打開這個(gè)文件添加兩個(gè)hosts

添加hosts

然后開兩個(gè)server 設(shè)置端口可以用:PORT=8001 node index.jsPORT=8002 node index.js
開兩個(gè)端口

端口1

端口2

下面我想讓frank.com的前端去訪問jack.com的后端。
在index.html里面的一句改成script.src = 'http://jack.com:8002/pay'
然后點(diǎn)擊frank.com的打錢,就訪問了jack.com的錢。

接下來的jsonp,上面的代碼中,在index.js里面。

response.write(`
    amount.innerText = amount.innerText - 1
  `)

這代碼的要求是jack.com的后端需要對(duì)frank.com的前端頁面細(xì)節(jié)了解,存在耦合。關(guān)系太緊密了,要解耦。
在index.js改成

response.write(`
        xxx.call(undefined,'success)
    `)

然后在html里寫好

window.xxx = function(result){
    alert('這是frank寫的前端代碼')
    alert(`我得到的結(jié)果是${result}`)
 }

這樣點(diǎn)擊按鈕的時(shí)候script.src = 'http://jack.com:8002/pay'就index.js不需要知道任何信息,調(diào)用xxx就行了,然后把結(jié)果(success或者fail)告訴前端,前端通過success或者fail告訴用戶失敗還是成功了。

代碼優(yōu)化和完全解耦

//html
script.src = 'http://jack.com:8002/pay?callbackName=xxx'

//index.js
response.write(`
      ${query.callbackName}.call(undefined,'success')
    `)

這樣index.js(后臺(tái))就不用關(guān)心細(xì)節(jié)了,給什么就調(diào)什么,這樣就完全耦合了,這就是jsonp。

response.write(`
      ${query.callbackName}.call(undefined,{
        "success": true,
        "left": ${newAmount}
      })
    `)

undefined后面的花括號(hào)是json,后面有個(gè)尾')',前面有代碼,叫做左padding和右padding,所以 json+padding = jsonp。jsonp就是這么來的。但是json有時(shí)候不是json,因?yàn)橐话阌胹tring比較多。
jsonp可以理解為動(dòng)態(tài)標(biāo)簽跨域請(qǐng)求。

buttonss.addEventListener('click',(e)=>{
    let script = document.createElement('script')
    let functionName = 'frank' + parseInt(Math.random()*100000,10)
    window[functionName] = function(result){
      if(result === 'success'){
        amount.innerText = amount.innerText - 1
      }else{

      }
    }
    script.src = 'http://jack.com:8002/pay?callback=' + functionName
    document.body.appendChild(script)  //img不需要這個(gè),但是script需要
    script.onload = function(e){
      e.currentTarget.remove()
      delete window[functionName]
    }
    script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
      delete window[functionName]
    }
  })

可以用jQuery簡(jiǎn)寫

 $.ajax({
   url: "http://jack.com:8002/pay",
   dataType: "jsonp",
   success: function( response ) {
       if(response === 'success'){
       amount.innerText = amount.innerText - 1
       }
   }
 })

這個(gè)方法ajax一點(diǎn)關(guān)系都沒有,它是個(gè)動(dòng)態(tài)script

代碼:https://github.com/xiaozhi1/nodejs-test
為什么JSONP 為什么不支持 POST
  • 因?yàn)閖sonp是通過動(dòng)態(tài)創(chuàng)建script實(shí)現(xiàn)的
  • 動(dòng)態(tài)創(chuàng)建script的時(shí)候只能用get,沒有辦法用post
JSONP

請(qǐng)求方:frank.com 的前端程序員(瀏覽器)
響應(yīng)方:jack.com 的后端程序員(服務(wù)器)

  • 請(qǐng)求方創(chuàng)建 script,src 指向響應(yīng)方,同時(shí)傳一個(gè)查詢參數(shù) ?callbackName=xxx
  • 響應(yīng)方根據(jù)查詢參數(shù)callbackName,構(gòu)造形如
    1.xxx.call(undefined, '你要的數(shù)據(jù)')
    2.xxx('你要的數(shù)據(jù)')
    這樣的響應(yīng)
  • 瀏覽器接收到響應(yīng),就會(huì)執(zhí)行 xxx.call(undefined, '你要的數(shù)據(jù)')
  • 那么請(qǐng)求方就知道了他要的數(shù)據(jù)
    這就是 JSONP
約定:
  • callbackName -> callback
  • xxx -> 隨機(jī)數(shù) frank12312312312321325()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容