JSONP手藝棧

簡樸的前後端交互

前面進修了這麼多,都是在和頁面打交道,不論是HTML、CSS、JavaScript,DOM,都沒有跑出瀏覽器,那本日來進修下和背景交互。

當我點擊付款按鈕時,頁面的數值會減小;當我革新頁面時,內容不會轉變,該怎樣完成呢?

先寫一個簡樸的node.js劇本,讓頁面能夠一般運轉

  1. 建立一個文件夾內里建立兩個文件
  2. 新建一個劇本文件
  3. 新建一個index.html文件
  4. 翻開服務器,就可以夠看到本身的頁面了

劇本文件

  var http = require('http')
  var fs = require('fs')
  var url = require('url')
  var port = process.argv[2]
  if(!port){
      console.log('請指定端口號好不啦?\nnode server.js 8888 如許不會嗎?')
      process.exit(1)
  }
    
  var server = http.createServer(function(request, response){
  var parsedUrl = url.parse(request.url, true)
  var pathWithQuery = request.url 
  var queryString = ''
  if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
  var path = parsedUrl.pathname
  var query = parsedUrl.query
  var method = request.method


  /******** 從這裏最先看,上面不要看 ************/

  if(path === '/'){
    var string = fs.readFileSync('./index.html','utf8')
    response.setHeader('Content-Type','text/html;charset=utf-8')
    response.write(string)
    response.end()
  }else if(path === '/css/style.css'){
    var string = fs.readFileSync('./css/style.css','utf8')
    response.setHeader('Content-Type','text/css')
    response.write(string)
    response.end()
  }else if(path === '/js/main.js'){
    var string = fs.readFileSync('./js/main.js','utf8')
    response.setHeader('Content-Type','text/javascript;charset=utf-8')
    response.write(string)
    response.end()
  }else{
    response.statusCode = 404
    response.setHeader('Content-Type','text/html;charset=utf-8')
    response.end('找不到對應的途徑,你需要自行修正 index.js')
  }
  
  /******** 代碼完畢,下面不要看 ************/
})

server.listen(port)
console.log('監聽 ' + port + ' 勝利\n請用在空中轉體720度然後用電飯煲翻開 http://localhost:' + port)

HTML 文件

<!DOCTYPE html>
<html>
<head>
    <title>首頁</title>
</head>
<body>
    <h5>你的餘額是<span id="amount">100</span></h5>
    <button id="button">付款</button>
    <script>
        let button = document.getElementById('button')
        let amount = document.getElementById('amount')
        
        button.addEventListener('click',function(){
            amount.textContent = +amount.textContent - 1
            console.log(1)
        })
    </script>
</body>
</html>

當我們點付款的時刻,100會往下減小,然則當我們革新頁面的時刻,又變成了100。

這裏我們只是在瀏覽器上面操縱頁面內容,數據沒有久長的存儲在數據庫內里,所以當我們革新頁面時,它又回到初始狀況,這明顯不是我們要的效果。

我們該怎樣處理這個題目呢?

用佔位符替換頁面數據

我們在適才的當前文件夾中新建一個文件,作為我們的數據庫,如今我們把100元寫在數據庫里。

數據庫簡樸的講就是一個能久長存儲數據的處所,文件是數據庫最簡樸的情勢。

touch db
echo '100' > db

index.html中的100用一個佔位符替換,這個佔位符要和頁面中其他變量不反覆,現實上頁面上的數據,前端是不需要曉得的,用一個佔位符站位即可,發送要求,後端程序員去讀數據庫里的內容,返回給前端。

<span id="amount">&&&amount&&&</span>

在劇本中if(path === '/index.html')里加一個替換佔位符語句。

var amount = fs.readFileSync('./index','utf8')        //文件中的數據類型是String
string = string.replace('&&&amount&&&',amount)

重啟服務器后,革新頁面后我們看到的100是數據庫里的數據,當我們點擊付款時,變化的是數據庫內里的數據,和前端沒有關係,然則當我們革新頁面后,照舊回到初始狀況,沒有保留最新的數據。

我點付款的時刻應當發送一個要求通知服務器,請把數據庫里的100變成99,然後革新頁面;我不對頁面做操縱轉變它的數據了,那我點付款的時刻,提議一個要求,應當怎樣做呢?

能夠發要求的標籤有imglinkscriptform表單,當我們點擊按鈕應當發送POST要求,由於是更新數據庫內容,所以這裏只需form表單能發送POST要求。

form表單發要求

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

在服務器內里增添一個/pay要求途徑。

if(path === '/pay' && method.toUpperCase() === 'POST'){
    var amount = fs.readFileSync('./db','utf8')
    var newAmount = amount - 1
    fs.writeFileSync('./db',newAmount)
    response.write('success')        //勝利后給用戶返回
    response.end()
}

當我點付款的時刻,會看當前頁面會跳轉到/pay途徑下的頁面,示意勝利了。

點擊瀏覽器的返回上一頁,革新下當前頁面,之前的100變成了99,不論怎樣革新或許從新翻開,數據都是之前的操縱過完畢后的數據。

這裏前端要寫的就是form表單,後端假如發現是某個途徑並且是POST要求,就去操縱數據庫。

這是舊時代的操縱,form表單一旦提交了都邑革新當前頁面,給用戶形成了不好的體驗。有個程序員想出了用iframe處理頁面革新的題目,操縱勝利后在iframe打卡跳轉頁面。

iframe 表單革新頁面

<form action="pay" method="post" target="success">
    <button id="button">付款</button>
</form>
<iframe name="success" src="about:blank" frameborder="0"></iframe>

有個程序員以為頁面中多出一個東西老是怪怪的,挖空心思又想出了動態建立img標籤的要領

動態建立img標籤發送要求

有潔癖的程序員老是能想出更好的處理要領,接着來看下動態建立img標籤的發送要求的要領。

劇本中增添/pay途徑下應當如許寫

 if(path === '/pay'){
      var string = fs.readFileSync('./db','utf8')
    var newAmount = string - 1
    if(Math.random() > 0.5){
        fs.writeFileSync('./db',newAmount)
        response.setHeader('Content-type','image/jpeg')
        response.statusCode = 200
        response.write(fs.readFileSync('./1.jpeg'))
    }else{
        response.statusCode = 400
        response.write('alert("fail")')
    }
    response.end()
  }

由於img標籤,只能發送GET要求,所以這裏就不做method判斷了。

JS 文件

button.addEventListener('click',function(e){
    let image = document.createElement('img')
    image.src = '/pay'
    img.onload = function(){
        alert('success')
        window.location.reload()
    }
    img.onerror = function(){
        alert('fail')
    }
})

onloadonerror是提醒用戶勝利了照樣失利,在onload內里加上一個window.location.reload()勝利後會自動革新頁面。

動態建立img標籤的要領,必需要返回實在的圖片,瀏覽器才曉得操縱勝利了,不然onload一向不會勝利,雖然數據庫已修正勝利了,但瀏覽器只需沒接收到圖片,就會實行onerror,這也是它的範圍地點——必需要返回真是圖片。

同時革新頁面會形成瀏覽器從新襯着,所以當瀏覽器接收到響應時,前端應當在頁面上自動減1,用戶並不會曉得這中心發生了什麼。

動態建立script標籤——SRJ計劃

a標籤發送要求太浪費資源了,這時候又有人想出了用script標籤發要求,這就是優異程序員和一般程序員之間的差異啊。

下面來看看是怎樣完成的:

button.addEventListener('click',function(){
    var script = document.createElement('script')
    script.src = '/pay'
    document.body.appendChild(script)    //必需要將建立出來的Script放在頁面中才夠
    script.onload = function(){
        alert('sucess')
    }
})

/pay途徑下的代碼

if(path === '/pay'){  
      var string = fs.readFileSync('./db','utf8')
    var newAmount = string - 1
    fs.writeFileSync('./db',newAmount)
    response.setHeader('Content-type','application/js')
    response.statusCode = 200
    response.write('alert("success1")')
    response.end()
  }

當我點付款時,勝利後起首實行劇本內里的script,實行完了以後才實行main.js內的onload

由於劇本中的script先實行,所以main.js內里就不需要提醒用戶了,直接背景給提醒內容就可以夠了。

以下:

response.write(`alert("success")
amount.innerText = amount.innerText -1`)    //ES6字符串要領

到這裏智慧的你應當也發現了一個題目,當我點付款時,不論勝利與否都邑建立一個script,關於程序員來說是沒法接收的,所以要用onloadonerror去監聽,不論勝利與否都將它刪撤除。這裏雖然刪掉了但它照樣在內存中。

script.onload = function(e){
    e.currentTarget.remove()
}
script.onerror = function(e){
    e.currentTarget.remove()
}

動態建立script要領發送要求叫做——SRJ計劃(全稱 Server rendered javascript),在 ajax 出來之前,無革新部分更新頁面內容的最好的計劃。

要求另一個網站的script

在頁面中引入一個script時,肯定要在當前域名嗎?

NO!!!我們在頁面中引入的種種庫,不都是引入他人的網站的script

那如許的話,是否是能夠操縱他人網站的/pay,所以GET要求太不平安,太輕易捏造了,所以大部分的/pay都用POST要求去做。

PORT=8002 node index.js 能夠開多個端口

SRJ計劃前後端耦合太嚴密了,需要後端對頁面相識太清晰。

實在前端供應一個xxx() API就可以夠了。

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

前端供應 API 的要領,實在解耦還沒有解的很清潔,我們在設置scriptsrc時能夠直接設置要求參數,劇本只需要取這個參數就可以夠了,至於詳細叫什麼名字不重要

script.src = 'http://baidu.com:8002/pay?callbackName=xxx'
response.write(`
    ${query.callbackName}.call(undefined,'success')
`)

到這裏已是很好的計劃了,然則有一個題目是,挪用函數通報的參數,前端怎樣曉得呢?假如不確定,到時刻出了題目就要各自扯皮了,這時候刻JSON應運而出。

JSONP計劃

JSONPJSON的花樣舉行參數通報,處理了兩個網站之間的交換。至於為何叫JSONP,應當是大括號左側的叫做左padding,右側的叫做右padding,連接起來就叫做JSONP

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

用筆墨敘說 JSONP

要求方:qq.com的前端程序員(瀏覽器)
響應方:baidu.com的後端程序員(服務器)

  1. 要求方建立script ,src指向響應方同事傳一個查詢參數 ?callbackName=xxx
  2. 響應方根據查詢參數callbackName,組織形如xxx.call(undefined,'你要的數據')如許的響應
  3. 瀏覽器接收到響應,xxx.call(undefined,'你要的數據')
  4. 那末要求方就曉得了它要的數據

這就是JSONP

商定:

  1. callbackName -> callback
  2. xxx -> 隨機數

根據商定寫一下

button.addEventListener('click',funcion(e){
    let script = document.createElement('script')
    let functionName = parseInt(Math.random()*1000000)        //這個函數名是隨機數
    window[functionName] = function(result){    //result是服務器返回的效果
        if(result === 'success'){
            amount.innerText = amount.innerText - 1
        }
    }

    script.src = 'http://baidu.com:8002?callback=' + functionName    //寫在參數內里
    document.body.appendChild(script)
    script.onload = function(e){
        e.currentTarget.remove()
        delete window[functionName]    //假如勝利了要幹掉這個函數
    }
    script.onerror = function(e){
        alert("false")
        e.currentTarget.remove()
        delete window[functionName]    //假如失利了也要幹掉這個函數

    }
})

jQuery完成

用jQuery就可以異常輕易的運用

button.addEventListener('click',function(){
    $.ajax({
        url: "http://baidu.com:8002/pay"
        dataType: "JSONP"
        success:function(response){
            console.log(response)
        }
    })
})

JSONP為何不支持POST要求

  1. 由於JSONP是經由過程動態建立script完成的
  2. 動態建立script只需GET要求沒有POST要求
    原文作者:UCCs
    原文地址: https://segmentfault.com/a/1190000015050698
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞