簡樸的前後端交互
前面進修了這麼多,都是在和頁面打交道,不論是HTML、CSS、JavaScript,DOM,都沒有跑出瀏覽器,那本日來進修下和背景交互。
當我點擊付款按鈕時,頁面的數值會減小;當我革新頁面時,內容不會轉變,該怎樣完成呢?
先寫一個簡樸的node.js劇本,讓頁面能夠一般運轉
- 建立一個文件夾內里建立兩個文件
- 新建一個劇本文件
- 新建一個index.html文件
- 翻開服務器,就可以夠看到本身的頁面了
劇本文件
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,然後革新頁面;我不對頁面做操縱轉變它的數據了,那我點付款的時刻,提議一個要求,應當怎樣做呢?
能夠發要求的標籤有img
、link
、script
、form
表單,當我們點擊按鈕應當發送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')
}
})
onload
和onerror
是提醒用戶勝利了照樣失利,在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
,關於程序員來說是沒法接收的,所以要用onload
和onerror
去監聽,不論勝利與否都將它刪撤除。這裏雖然刪掉了但它照樣在內存中。
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 的要領,實在解耦還沒有解的很清潔,我們在設置script
的src
時能夠直接設置要求參數,劇本只需要取這個參數就可以夠了,至於詳細叫什麼名字不重要
script.src = 'http://baidu.com:8002/pay?callbackName=xxx'
response.write(`
${query.callbackName}.call(undefined,'success')
`)
到這裏已是很好的計劃了,然則有一個題目是,挪用函數通報的參數,前端怎樣曉得呢?假如不確定,到時刻出了題目就要各自扯皮了,這時候刻JSON
應運而出。
JSONP計劃
JSONP
用JSON
的花樣舉行參數通報,處理了兩個網站之間的交換。至於為何叫JSONP
,應當是大括號左側的叫做左padding
,右側的叫做右padding
,連接起來就叫做JSONP
。
${query.callbackName}.call(undefined,{
"success": true
"left": ${newAmount}
})
用筆墨敘說 JSONP
要求方:qq.com
的前端程序員(瀏覽器)
響應方:baidu.com
的後端程序員(服務器)
- 要求方建立
script
,src
指向響應方同事傳一個查詢參數?callbackName=xxx
- 響應方根據查詢參數
callbackName
,組織形如xxx.call(undefined,'你要的數據')
如許的響應 - 瀏覽器接收到響應,
xxx.call(undefined,'你要的數據')
- 那末要求方就曉得了它要的數據
這就是JSONP
商定:
- callbackName -> callback
- 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
要求
- 由於
JSONP
是經由過程動態建立script
完成的 - 動態建立
script
只需GET要求沒有POST
要求