摘要:表單發(fā)請(qǐng)求付款在服務(wù)器里面增加一個(gè)請(qǐng)求路徑。動(dòng)態(tài)創(chuàng)建方法發(fā)送請(qǐng)求叫做方案全稱(chēng),在出來(lái)以前,無(wú)刷新局部更新頁(yè)面內(nèi)容的最好的方案。
簡(jiǎn)單的前后端交互
前面學(xué)習(xí)了這么多,都是在和頁(yè)面打交道,不管是HTML、CSS、JavaScript,DOM,都沒(méi)有跑出瀏覽器,那今天來(lái)學(xué)習(xí)下和后臺(tái)交互。
當(dāng)我點(diǎn)擊付款按鈕時(shí),頁(yè)面的數(shù)值會(huì)減小;當(dāng)我刷新頁(yè)面時(shí),內(nèi)容不會(huì)改變,該怎么實(shí)現(xiàn)呢?
先寫(xiě)一個(gè)簡(jiǎn)單的node.js腳本,讓頁(yè)面能夠正常運(yùn)行
創(chuàng)建一個(gè)文件夾里面創(chuàng)建兩個(gè)文件
新建一個(gè)腳本文件
新建一個(gè)index.html文件
打開(kāi)服務(wù)器,就可以看到自己的頁(yè)面了
腳本文件var http = require("http") var fs = require("fs") var url = require("url") var port = process.argv[2] if(!port){ console.log("請(qǐng)指定端口號(hào)好不啦? node 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 /******** 從這里開(kāi)始看,上面不要看 ************/ 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("找不到對(duì)應(yīng)的路徑,你需要自行修改 index.js") } /******** 代碼結(jié)束,下面不要看 ************/ }) server.listen(port) console.log("監(jiān)聽(tīng) " + port + " 成功 請(qǐng)用在空中轉(zhuǎn)體720度然后用電飯煲打開(kāi) http://localhost:" + port)HTML 文件
首頁(yè) 你的余額是100
當(dāng)我們點(diǎn)付款的時(shí)候,100會(huì)往下減小,但是當(dāng)我們刷新頁(yè)面的時(shí)候,又變成了100。
這里我們只是在瀏覽器上面操作頁(yè)面內(nèi)容,數(shù)據(jù)沒(méi)有長(zhǎng)久的存儲(chǔ)在數(shù)據(jù)庫(kù)里面,所以當(dāng)我們刷新頁(yè)面時(shí),它又回到初始狀態(tài),這顯然不是我們要的效果。
我們?cè)撛趺唇鉀Q這個(gè)問(wèn)題呢?
用占位符替換頁(yè)面數(shù)據(jù)我們?cè)趧偛诺漠?dāng)前文件夾中新建一個(gè)文件,作為我們的數(shù)據(jù)庫(kù),現(xiàn)在我們把100元寫(xiě)在數(shù)據(jù)庫(kù)里。
數(shù)據(jù)庫(kù)簡(jiǎn)單的講就是一個(gè)能長(zhǎng)久存儲(chǔ)數(shù)據(jù)的地方,文件是數(shù)據(jù)庫(kù)最簡(jiǎn)單的形式。
touch db echo "100" > db
index.html中的100用一個(gè)占位符替代,這個(gè)占位符要和頁(yè)面中其他變量不重復(fù),實(shí)際上頁(yè)面上的數(shù)據(jù),前端是不需要知道的,用一個(gè)占位符站位即可,發(fā)送請(qǐng)求,后端程序員去讀數(shù)據(jù)庫(kù)里的內(nèi)容,返回給前端。
&&&amount&&&
在腳本中if(path === "/index.html")里加一個(gè)替換占位符語(yǔ)句。
var amount = fs.readFileSync("./index","utf8") //文件中的數(shù)據(jù)類(lèi)型是String string = string.replace("&&&amount&&&",amount)
重啟服務(wù)器后,刷新頁(yè)面后我們看到的100是數(shù)據(jù)庫(kù)里的數(shù)據(jù),當(dāng)我們點(diǎn)擊付款時(shí),變化的是數(shù)據(jù)庫(kù)里面的數(shù)據(jù),和前端沒(méi)有關(guān)系,但是當(dāng)我們刷新頁(yè)面后,依舊回到初始狀態(tài),沒(méi)有保存最新的數(shù)據(jù)。
我點(diǎn)付款的時(shí)候應(yīng)該發(fā)送一個(gè)請(qǐng)求告訴服務(wù)器,請(qǐng)把數(shù)據(jù)庫(kù)里的100變成99,然后刷新頁(yè)面;我不對(duì)頁(yè)面做操作改變它的數(shù)據(jù)了,那我點(diǎn)付款的時(shí)候,發(fā)起一個(gè)請(qǐng)求,應(yīng)該怎么做呢?
可以發(fā)請(qǐng)求的標(biāo)簽有img、link、script、form表單,當(dāng)我們點(diǎn)擊按鈕應(yīng)該發(fā)送POST請(qǐng)求,因?yàn)槭歉聰?shù)據(jù)庫(kù)內(nèi)容,所以這里只有form表單能發(fā)送POST請(qǐng)求。
form表單發(fā)請(qǐng)求在服務(wù)器里面增加一個(gè)/pay請(qǐng)求路徑。
if(path === "/pay" && method.toUpperCase() === "POST"){ var amount = fs.readFileSync("./db","utf8") var newAmount = amount - 1 fs.writeFileSync("./db",newAmount) response.write("success") //成功后給用戶(hù)返回 response.end() }
當(dāng)我點(diǎn)付款的時(shí)候,會(huì)看當(dāng)前頁(yè)面會(huì)跳轉(zhuǎn)到/pay路徑下的頁(yè)面,表示成功了。
點(diǎn)擊瀏覽器的返回上一頁(yè),刷新下當(dāng)前頁(yè)面,之前的100變成了99,無(wú)論怎么刷新或者重新打開(kāi),數(shù)據(jù)都是之前的操作過(guò)結(jié)束后的數(shù)據(jù)。
這里前端要寫(xiě)的就是form表單,后端如果發(fā)現(xiàn)是某個(gè)路徑并且是POST請(qǐng)求,就去操作數(shù)據(jù)庫(kù)。
這是舊時(shí)代的操作,form表單一旦提交了都會(huì)刷新當(dāng)前頁(yè)面,給用戶(hù)造成了不好的體驗(yàn)。有個(gè)程序員想出了用iframe解決頁(yè)面刷新的問(wèn)題,操作成功后在iframe打卡跳轉(zhuǎn)頁(yè)面。
iframe 表單刷新頁(yè)面有個(gè)程序員覺(jué)得頁(yè)面中多出一個(gè)東西總是怪怪的,絞盡腦汁又想出了動(dòng)態(tài)創(chuàng)建img標(biāo)簽的方法
動(dòng)態(tài)創(chuàng)建img標(biāo)簽發(fā)送請(qǐng)求有潔癖的程序員總是能想出更好的解決方法,接著來(lái)看下動(dòng)態(tài)創(chuàng)建img標(biāo)簽的發(fā)送請(qǐng)求的方法。
腳本中增加/pay路徑下應(yīng)該這樣寫(xiě)
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() }
因?yàn)?b>img標(biāo)簽,只能發(fā)送GET請(qǐng)求,所以這里就不做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是提示用戶(hù)成功了還是失敗,在onload里面加上一個(gè)window.location.reload()成功后會(huì)自動(dòng)刷新頁(yè)面。
動(dòng)態(tài)創(chuàng)建img標(biāo)簽的方法,必須要返回真實(shí)的圖片,瀏覽器才能知道操作成功了,不然onload一直不會(huì)成功,雖然數(shù)據(jù)庫(kù)已經(jīng)修改成功了,但瀏覽器只要沒(méi)接收到圖片,就會(huì)執(zhí)行onerror,這也是它的局限所在——必須要返回真是圖片。
同時(shí)刷新頁(yè)面會(huì)造成瀏覽器重新渲染,所以當(dāng)瀏覽器接收到響應(yīng)時(shí),前端應(yīng)該在頁(yè)面上自動(dòng)減1,用戶(hù)并不會(huì)知道這中間發(fā)生了什么。
動(dòng)態(tài)創(chuàng)建script標(biāo)簽——SRJ方案用a標(biāo)簽發(fā)送請(qǐng)求太浪費(fèi)資源了,這時(shí)又有人想出了用script標(biāo)簽發(fā)請(qǐng)求,這就是優(yōu)秀程序員和普通程序員之間的差距啊。
下面來(lái)看看是怎么實(shí)現(xiàn)的:
button.addEventListener("click",function(){ var script = document.createElement("script") script.src = "/pay" document.body.appendChild(script) //必須要將創(chuàng)建出來(lái)的Script放在頁(yè)面中才可以 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() }
當(dāng)我點(diǎn)付款時(shí),成功后首先執(zhí)行腳本里面的script,執(zhí)行完了之后才執(zhí)行main.js內(nèi)的onload。
因?yàn)槟_本中的script先執(zhí)行,所以main.js里面就不需要提示用戶(hù)了,直接后臺(tái)給提示內(nèi)容就可以了。
如下:
response.write(`alert("success") amount.innerText = amount.innerText -1`) //ES6字符串方法
到這里聰明的你應(yīng)該也發(fā)現(xiàn)了一個(gè)問(wèn)題,當(dāng)我點(diǎn)付款時(shí),不管成功與否都會(huì)創(chuàng)建一個(gè)script,對(duì)于程序員來(lái)說(shuō)是無(wú)法接受的,所以要用onload和onerror去監(jiān)聽(tīng),不管成功與否都將它刪除掉。這里雖然刪掉了但它還是在內(nèi)存中。
script.onload = function(e){ e.currentTarget.remove() } script.onerror = function(e){ e.currentTarget.remove() }
動(dòng)態(tài)創(chuàng)建script方法發(fā)送請(qǐng)求叫做——SRJ方案(全稱(chēng) Server rendered javascript),在 ajax 出來(lái)以前,無(wú)刷新局部更新頁(yè)面內(nèi)容的最好的方案。
請(qǐng)求另一個(gè)網(wǎng)站的script在頁(yè)面中引入一個(gè)script時(shí),一定要在當(dāng)前域名嗎?
NO?。。∥覀?cè)陧?yè)面中引入的各種庫(kù),不都是引入別人的網(wǎng)站的script嘛
那這樣的話(huà),是不是可以操作別人網(wǎng)站的/pay,所以GET請(qǐng)求太不安全,太容易偽造了,所以大部分的/pay都用POST請(qǐng)求去做。
PORT=8002 node index.js 可以開(kāi)多個(gè)端口
SRJ方案前后端耦合太緊密了,需要后端對(duì)頁(yè)面了解太清楚。
其實(shí)前端提供一個(gè)xxx() API就可以了。
response.write(` xxx.call(undefined,"success") `)
前端提供 API 的方法,其實(shí)解耦還沒(méi)有解的很干凈,我們?cè)谠O(shè)置script的src時(shí)可以直接設(shè)置請(qǐng)求參數(shù),腳本只需要取這個(gè)參數(shù)就可以了,至于具體叫什么名字不重要
script.src = "http://baidu.com:8002/pay?callbackName=xxx"
response.write(` ${query.callbackName}.call(undefined,"success") `)
到這里已經(jīng)是很好的方案了,但是有一個(gè)問(wèn)題是,調(diào)用函數(shù)傳遞的參數(shù),前端怎么知道呢?如果不確定,到時(shí)候出了問(wèn)題就要各自扯皮了,這時(shí)候JSON應(yīng)運(yùn)而出。
JSONP方案JSONP用JSON的格式進(jìn)行參數(shù)傳遞,解決了兩個(gè)網(wǎng)站之間的交流。至于為什么叫JSONP,應(yīng)該是大括號(hào)左邊的叫做左padding,右邊的叫做右padding,連接起來(lái)就叫做JSONP。
${query.callbackName}.call(undefined,{ "success": true "left": ${newAmount} })用文字?jǐn)⑹?JSONP
請(qǐng)求方:qq.com的前端程序員(瀏覽器)
響應(yīng)方:baidu.com的后端程序員(服務(wù)器)
請(qǐng)求方創(chuàng)建script ,src指向相應(yīng)方同事傳一個(gè)查詢(xún)參數(shù) ?callbackName=xxx
響應(yīng)方根據(jù)查詢(xún)參數(shù)callbackName,構(gòu)造形如xxx.call(undefined,"你要的數(shù)據(jù)")這樣的響應(yīng)
瀏覽器接收到響應(yīng),xxx.call(undefined,"你要的數(shù)據(jù)")
那么請(qǐng)求方就知道了它要的數(shù)據(jù)
這就是JSONP
約定:callbackName -> callback
xxx -> 隨機(jī)數(shù)
按照約定寫(xiě)一下
button.addEventListener("click",funcion(e){ let script = document.createElement("script") let functionName = parseInt(Math.random()*1000000) //這個(gè)函數(shù)名是隨機(jī)數(shù) window[functionName] = function(result){ //result是服務(wù)器返回的結(jié)果 if(result === "success"){ amount.innerText = amount.innerText - 1 } } script.src = "http://baidu.com:8002?callback=" + functionName //寫(xiě)在參數(shù)里面 document.body.appendChild(script) script.onload = function(e){ e.currentTarget.remove() delete window[functionName] //如果成功了要干掉這個(gè)函數(shù) } script.onerror = function(e){ alert("false") e.currentTarget.remove() delete window[functionName] //如果失敗了也要干掉這個(gè)函數(shù) } })jQuery實(shí)現(xiàn)
用jQuery就能非常方便的使用
button.addEventListener("click",function(){ $.ajax({ url: "http://baidu.com:8002/pay" dataType: "JSONP" success:function(response){ console.log(response) } }) })JSONP為什么不支持POST請(qǐng)求
因?yàn)?b>JSONP是通過(guò)動(dòng)態(tài)創(chuàng)建script實(shí)現(xiàn)的
動(dòng)態(tài)創(chuàng)建script只有GET請(qǐng)求沒(méi)有POST請(qǐng)求
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95290.html
摘要:希望幫助更多的前端愛(ài)好者學(xué)習(xí)。前端開(kāi)發(fā)者指南作者科迪林黎,由前端大師傾情贊助。翻譯最佳實(shí)踐譯者張捷滬江前端開(kāi)發(fā)工程師當(dāng)你問(wèn)起有關(guān)與時(shí),老司機(jī)們首先就會(huì)告訴你其實(shí)是個(gè)沒(méi)有網(wǎng)絡(luò)請(qǐng)求功能的庫(kù)。 前端基礎(chǔ)面試題(JS部分) 前端基礎(chǔ)面試題(JS部分) 學(xué)習(xí) React.js 比你想象的要簡(jiǎn)單 原文地址:Learning React.js is easier than you think 原文作...
摘要:隨后也跟進(jìn)抄襲了,取名,并被納入規(guī)范全稱(chēng)翻譯成中文異步的和技術(shù)的核心是對(duì)象簡(jiǎn)稱(chēng),可以在不刷新頁(yè)面頁(yè)面也能取得新的數(shù)據(jù)。注意請(qǐng)求和響應(yīng)都不包含信息。 JSONP發(fā)展 了解了JSONP技術(shù)棧后,知道了JSONP是AJAX出現(xiàn)之前后端交互最好的解決方案,但它依然沒(méi)解決問(wèn)題,用JSONP只能發(fā)送GET請(qǐng)求,不能發(fā)其他請(qǐng)求 form表單可以發(fā)GET請(qǐng)求,也可以發(fā)POST請(qǐng)求,POST請(qǐng)求沒(méi)有請(qǐng)求...
摘要:概述項(xiàng)目是基于,成品是一個(gè)移動(dòng)端的音樂(lè)播放器,來(lái)源于的實(shí)戰(zhàn)課程。播放器是全局組件,放在下面,通過(guò)傳遞數(shù)據(jù),觸發(fā)提交,從而使播放器開(kāi)始工作。將請(qǐng)求的數(shù)據(jù)格式化后再通過(guò)傳遞,組件間共享,實(shí)現(xiàn)歌曲的播放切換等。 概述 項(xiàng)目是基于Vue.js,成品是一個(gè)移動(dòng)端的音樂(lè)播放器,來(lái)源于imooc的實(shí)戰(zhàn)課程。自己動(dòng)手實(shí)踐并加以修改拓展。項(xiàng)目的大致流程是Vue-cli構(gòu)建開(kāi)發(fā)環(huán)境,分析需求,設(shè)計(jì)構(gòu)思,規(guī)...
摘要:寫(xiě)在前面沒(méi)錯(cuò),這就是慕課網(wǎng)上的那個(gè)音樂(lè)播放器,后臺(tái)是某音樂(lè)播放器的線上接口扒取,雖然這類(lèi)項(xiàng)目寫(xiě)的人很多,但不得不說(shuō)這還是個(gè)少有的適合提升的好項(xiàng)目,做這個(gè)項(xiàng)目除了想寫(xiě)一個(gè)比較大并且功能復(fù)雜的項(xiàng)目,主要原因是要拿它來(lái)應(yīng)對(duì)面試,也確實(shí)對(duì)我的業(yè)務(wù)能 寫(xiě)在前面 沒(méi)錯(cuò),這就是慕課網(wǎng)上的那個(gè)vue音樂(lè)播放器,后臺(tái)是某音樂(lè)播放器的線上接口扒取,雖然這類(lèi)項(xiàng)目寫(xiě)的人很多,但不得不說(shuō)這還是個(gè)少有的適合vu...
閱讀 1902·2021-11-15 11:46
閱讀 1102·2021-10-26 09:49
閱讀 1835·2021-10-14 09:42
閱讀 3394·2021-09-26 09:55
閱讀 846·2019-08-30 13:58
閱讀 1045·2019-08-29 16:40
閱讀 3481·2019-08-26 10:27
閱讀 616·2019-08-23 18:18