首先讓我們來先寫一個(gè)server
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 這樣不會(huì)嗎袭灯?')
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
/******** 從這里開始看记焊,上面不要看 ************/
console.log('包含查詢字符串的路徑\n' + pathWithQuery)
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 === '/style.css'){
var string = fs.readFileSync('./style.css','utf8')
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.setHeader('Content-Type','application/javascript')
response.write(string)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type','text/html;charset=utf-8')
response.write('找不到對應(yīng)的路徑屑埋,需要先自行修改')
response.end()
}
/******** 代碼結(jié)束,下面不要看 ************/
})
server.listen(port)
console.log('監(jiān)聽 ' + port + ' 成功\n請用在空中轉(zhuǎn)體720度然后用電飯煲打開 http://localhost:' + port)
html代碼
<p>您的余額是<span id="amount">&&amount&&</span></p>
<button id='click'>Click</button>
<script src="./main.js"></script>
main.js代碼
click.addEventListener('click',function(e){
amount.innerText = amount.innerText - 1
})
返回html會(huì)同時(shí)返回里面內(nèi)聯(lián)的main.js文件和style.css
當(dāng)我們點(diǎn)擊一下余額數(shù)字就會(huì)減一,但是如果刷新頁面烛愧,數(shù)字就會(huì)重新回到100,所以我需要一個(gè)數(shù)據(jù)庫來存放余額
我們給余額添加一個(gè)數(shù)據(jù)庫掂碱,首先新建一個(gè)bd文件怜姿,然后把100寫進(jìn)去,然后修改server.js
if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./bd','utf-8') //讀取bd文件里面的amount值
string = string.replace('&&amount&&',amount) //用amount來替換html里面的&&amount&&
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
<p>您的余額是<span id="amount">&&amount&&</span></p>
接著我們要告訴服務(wù)器疼燥,每當(dāng)我點(diǎn)一下button你都去拿bd里的數(shù)字沧卢,那么我們就要發(fā)一個(gè)post請求了,那我們用form來發(fā)一個(gè)請求吧
<form action="/pay" method="post">
<input type="submit" value="付款">
</form>
然后server處理這個(gè)請求
else if (path === "/pay" && method.toUpperCase() === 'POST') {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
if (Math.random() > 0.5) {
fs.writeFileSync('./bd', newAmount)
response.write('success')
} else {
response.write('fail')
}
response.end()
}
form表單提交醉者,每次點(diǎn)擊后success和fail刷新后顯示搏恤,想要看到余額變化需要返回再刷新,于是我們可以添加一個(gè)iframe來承載success和fail湃交,讓他在iframe里面刷新熟空,而不是當(dāng)前頁面
<form action="/pay" method="post" target="result">
<input type="submit" value="付款">
</form>
<iframe src="about:blank" name="result" frameborder="0"></iframe>
但是這樣體驗(yàn)還是很差,用戶仍需要刷新才能看到余額變化搞莺,而且還要加一個(gè)iframe息罗,那么能不能不用iframe,發(fā)請求除了用form還能用什么呢才沧?css的link,a標(biāo)簽迈喉,img標(biāo)簽和script標(biāo)簽都可以發(fā)請求。
先嘗試動(dòng)態(tài)創(chuàng)建一個(gè)圖片來發(fā)請求温圆。
main.js 代碼
click.addEventListener('click',function(e){
let image = document.createElement('img')
image.src = '/pay'
})
這種方法沒用辦法去post,一定會(huì)去get.而且我們還不知道他成功沒成功挨摸,我們可以通過image.onload()和image.onerror()方法,并且在方法內(nèi)部判斷返回的狀態(tài)碼來確定成功和失敗
main.js代碼
click.addEventListener('click',function(e){
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打錢成功')
amount.innerText = amount.innerText - 1
}
image.onerror = function(){
alert('打錢失敗')
}
})
server代碼
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
if (Math.random() > 0.5) {
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type','image/jpg')
response.statusCode =200
response.write(fs.readFileSync('./dog.jpg'))
} else {
response.statusCode =400
response.write('fail')
}
response.end()
}
現(xiàn)在實(shí)現(xiàn)了無刷新的更新岁歉,但是沒有辦法post得运。我們再來嘗試一下script發(fā)請求
main.js代碼
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('success')
}
script.onerror = function(){
alert('fail')
}
})
server代碼
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write('alert("我是pay")')
response.end()
}
但是每次都會(huì)在html里面創(chuàng)建一個(gè)會(huì)執(zhí)行的script
我們可以把onload()的內(nèi)容寫到server里面
response.write(`
alert("success")
window.location.reload()
`)
這樣服務(wù)器返回了一個(gè)在瀏覽器里執(zhí)行的代碼,但是刷新體驗(yàn)不好,那么我們可以在服務(wù)器上改amount
response.write(`
alert("success")
amount.innerText = amount.innerText - 1
`)
每次打錢都會(huì)新建一個(gè)script熔掺,我們當(dāng)然要?jiǎng)h掉他們
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
這里有一個(gè)重要的概念饱搏,就是script的src可以請求不同的域名,我們做兩個(gè)網(wǎng)站置逻,讓他們彼此交流 frank.com:8001和jack.com:8002
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
jack.com操作它的數(shù)據(jù)庫打錢
但是這里有一個(gè)問題推沸,就是jack.com的程序員需要對frank.com的頁面細(xì)節(jié)了解的很徹底,有很大的耦合性
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write(`
${query.callbackName}.call(undefined,'success')
`)
response.end()
}
window.yyy = function(result){
alert(`我得到的結(jié)果是${result}`)
}
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay?callbackName=yyy'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
我們把回調(diào)函數(shù)的名字寫在callbackName里面,服務(wù)器通過query.callbackName.call()來調(diào)用前端寫好的函數(shù)券坞,如果把'success'換成JSON那么就是JSONP了鬓催!
response.write(`
${query.callbackName}.call(undefined,{
"success":true,
"left":${newAmount}
})
`) // JSON + Padding 叫做JSONP
xxx一般用一個(gè)隨機(jī)數(shù)代替
let script = document.createElement('script')
let functionName = 'frank'+parseInt(Math.random()*1000000,10)
window[functionName] = function(result){
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else{
}
}
當(dāng)我調(diào)用完這個(gè)函數(shù)后,就刪除這個(gè)函數(shù)
script.onload = function(e){
e.currentTarget.remove()
delete window[functionName]
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
delete window[functionName]
}
其實(shí)jQuery已經(jīng)封裝好了
click.addEventListener("click", function(e) {
// 使用jQuery
$.ajax({
url: "http://jack.com:8002/pay",
jsonp: "callback",
dataType: "jsonp",
success: function( response ) {
if(response === 'success') {
amount.innerText = amount.innerText - 1
}
}
});
問: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