有時候,咱們須要長久的存儲數據,不隨着外界因素的改變而改變,這樣的數據棲息地能夠稱之爲數據庫。javascript
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">100</span></p> <button id='button'>付款1塊</button> </body> <script> $('#button').on('click',() => { let n=document.getElementById('amount').innerText; n -= 1; document.getElementById('amount').innerText = n; }) </script> </html>
之因此稱之爲表面,是由於,全部的操做都是表面現象,當頁面刷新時,一切都會回到初始狀態。並且,別人很容易就可以獲取你的信息。最好是,咱們去調取數據庫裏你的信息,而後顯示在頁面上。html
以往,咱們是在前端的代碼中去改用戶的餘額。如今,不,咱們去後端直接改數據庫裏的餘額,而後,把後端數據庫的金額顯示在前端頁面上。而前端用來向後端發起一個請求,請求更改數據庫裏的餘額。前端
<button>
顯然不能發起一個請求,因此使用<form>
,表單是能夠發起一個請求的,並且能夠指定請求的方式:get
/post
。java
前端代碼:jquery
<form>
發起一個請求action='/pay'
method='post'
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <form id='button' action='/pay' method='post'> <input type='submit' value='付款1塊錢'> </form> </body> </html>
使用Javascript寫的後端代碼:ajax
response.write('success');
//讀取數據庫金額,並顯示在頁面上 if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); //修改數據庫中金額,並返回success }else if(path === '/pay' && method === 'post'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.write('success'); response.end(); }
點擊按鈕:數據庫
點擊付款1塊錢,發送以下請求,而且頁面中顯示success。說明請求成功,數據庫中金額修改爲功!json
返回頁面:後端
查看數據庫:服務器
有沒有發現,咱們每一次付款,都須要返回、刷新頁面才能看到本身正確的餘額。用戶體驗極差!
而且,<form>
表單自帶特性:每一次提交,都會刷新頁面。
最好是,付款以後,頁面只有數字那部分局部刷新。
<iframe>
的功能就是在頁面中單開一個空間做爲新的頁面,咱們能夠將<form>
關聯到這個<iframe>
上,每次提交都只刷新<iframe>
便可。
<body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <form id='button' action='/pay' method='post' target='result'> <input type='submit' value='付款1塊錢'> </form> <iframe name='result' src='about:blank' frameborder='0' height=200></iframe> </body>
過期。雖然不須要再按返回了,可是仍是須要刷新頁面才能看到真正的餘額。
既然,咱們不但願頁面刷新,那咱們就得換一個方式發送請求。鐺鐺鐺鐺!這就是<img>
!不過<img>
只能發送get
請求,不過,方法先進,用戶體驗良好,get
也能夠接受。
前端代碼:
<button>
,建立一個<img>
,路徑指向後臺文件/pay
image.onload=function(){}
,提示成功,並將金額amount.innerText -= 1
image.onerror=function(){}
,提示失敗<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <button id='button'>付款1塊錢</button>> </body> <script> $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('img'); image.src='./pay'; image.onload=function(){ alert('success'); amount.innerText -= 1; } image.onerror=function(){ alert('fail'); } }) </script>
使用Javascript寫的後端代碼:
response.write(fs.readFileSync(你的圖片路徑));
//和以前同樣,讀取數據庫中金額,並顯示在頁面上,但這裏並不必定會用到這些代碼,由於咱們不須要刷新頁面了 if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); } //修改數據庫中金額,成功後返回一張圖片 else if(path === '/pay'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.setHeader('Content-type','image/jpg'); response.write(fs.readFileSync(你的圖片路徑)); response.end(); }
alert()
,不像以前須要新的頁面<img>
標籤,也成功獲取了一張圖片,可是圖片並無顯示出來,由於,咱們只是在js裏建立了<img>
標籤,並無把它加到頁面裏,沒有append()
到哪裏去。因此,咱們利用了<img>
可以發送請求的特色,而沒有真正的顯示出圖片。哇!這樣已經很好了,能夠返回一個大小很小的圖片呀,或者什麼亂起八糟的圖片。可是,圖片畢竟是圖片,返回一張圖片會影響響應速度。
咱們發現,<script>
竟然也能發送請求,那更好了,<script>
請求返回的是空字符串,那可比圖片小多了。
前端代碼:
<script>
,指定<script>
路徑爲./pay
<img>
發送請求不同在於:<script>
須要加載到<body>
才能生效<script>
加載成功:script.onload=function(){}
<script>
加載失敗:script.onerror=function(){}
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <button id='button'>付款1塊錢</button>> </body> <script> $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('script'); script.src='./pay'; document.body.appendChild(script); script.onload=function(){ alert('success'); } script.onerror=function(){ alert('fail'); } }) </script>
使用Javascript寫的後端代碼:
response.write('');
//和<img>發送請求時的沒有區別 if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); //修改數據庫中金額,成功後返回空字符串 }else if(path === '/pay'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.setHeader('Content-type','application/javascript'); response.write(''); response.end(); }
在使用<script>
發送請求的本例中,咱們沒有在前端代碼中加入,當script.onload=function(){}
時,頁面中數字減一amount.innerText -= 1;
。這是由於,通過測試,
在前端代碼中加入:
script.onload=function(){ //以前的代碼 console.log('我是前端'); }
在後端代碼中加入:
else if(path === '/pay'){ //以前的代碼 response.write('console.log("我是後端")'); //以後的代碼 }
結果發現,首先出現後端的console.log("我是後端")
,再出現前端的console.log('我是前端')
。
以前,咱們將amount.innerText -= 1;
放在前端代碼裏是由於,以前使用<img>
,返回只能是一張圖片;如今咱們使用<script>
,返回值會被當作Javascript執行。
因此,咱們直接在後端代碼輸入咱們想要執行的前端代碼就行啦👍👍👍
改進後的後端代碼:
response.write()
部分⭐⭐⭐<Script> if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); }else if(path === '/pay'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.setHeader('Content-type','application/javascript'); response.write(` // console.log("我是後端"); amount.innerText -= 1; // alert("success"); `); response.end(); } </Script>
只有一個問題了(๑•̀ㅂ•́)و✧,使用<script>
發送請求會在頁面中實際的添加<script></script>
標籤才能生效,因此,點多少次按鈕就會添加多少個<script></script>
標籤。
修改後的前端代碼:
<script>
加載成功後,刪除這個標籤e.currentTarget.remove();
<script>
加載失敗,刪除這個標籤e.currentTarget.remove();
<script> $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('script'); script.src='./pay'; document.body.appendChild(script); script.onload=function(e){ // alert('success'); e.currentTarget.remove(); } script.onerror=function(){ // alert('fail'); e.currentTarget.remove(); } }) </script>
不錯👍👍👍
爲何要在前端代碼裏優化這個問題呢??由於先執行的是後端代碼,最後執行的是前端代碼,功能是先在後端實現的,因此,在前端裏刪除這個標籤也不會對功能產生什麼影響。
前端代碼:
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <button id='button'>付款1塊錢</button>> </body> <script> $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('script'); script.src='./pay'; document.body.appendChild(script); script.onload=function(e){ // alert('success'); e.currentTarget.remove(); } script.onerror=function(){ // alert('fail'); e.currentTarget.remove(); } }) </script>
使用Javascript寫的後端代碼:
if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); }else if(path === '/pay'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.setHeader('Content-type','application/javascript'); response.write(` // console.log("我是後端"); amount.innerText -= 1; // alert("success"); `); response.end(); }
該方法稱之爲SRJ:Server Rendered Javascript,即後端返回Javascript。
在功能上,上述代碼很是好的實現了前端與後端交互,調用數據庫數據顯示在頁面上。可是,在職能上,先後端仍是有耦合的現象,在這裏,後端裏寫進了前端代碼amount.innerText -= 1;
。因此,此時,咱們須要解耦。
解耦的思路很簡單,將服務器上的前端代碼都放到前端的一個函數裏,而後,後端只須要調用這個函數就OK了。
使用Javascript寫的後端代碼:
將下面這段:
if(path === './'){ //your code }else if(path === '/pay'){ //your code response.write(` // console.log("我是後端"); amount.innerText -= 1; // alert("success"); `); response.end(); }
修改成:
if(path === './'){ //your code }else if(path === '/pay'){ //your code response.write(` ${query.callbackName}.call(undefined,'success'); //👈👈新添加 `); response.end(); }
前端代碼:
在<script>
添加函數,而且,在<script>
裏添加請求參數?callbackName=xxx
:
<!DOCTYPE html> <!--your code--> <script> window.yyy=function(status){ //👈👈新添加 if(status === 'success'){ amount.innerText -= 1; } else if(){} } $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('script'); script.src='./pay?callbackName=yyy'; //👈👈新添加 document.body.appendChild(script); script.onload=function(e){ // alert('success'); e.currentTarget.remove(); } script.onerror=function(){ // alert('fail'); e.currentTarget.remove(); } }) </script>
解釋:
如今,前端裏新添加的<script>
的地址裏傳入了參數?callbackName=yyy
,而且添加了回調函數window.yyy=function(){}
。後端裏,使用${query.callbackName}
來查找地址裏?callbackName
的所指,而後使用call()
來調用所指的yyy
函數。
效果:
看下後端代碼:
if(path === './'){ //your code }else if(path === '/pay'){ //your code response.write(` ${query.callbackName}.call(undefined,'success'); //👈👈注意!!! `); response.end(); }
看到👈👈注意!!!的那一行,咱們回調時傳入的參數是'success'
,是個字符串,字符串前面的內容咱們稱之爲左Padding,後面的內容咱們稱之爲右Padding。當傳入的參數是個JSON的時候,咱們就稱之爲JSON+Padding,JSONP。
if(path === './'){ //your code }else if(path === '/pay'){ //your code response.write(` ${query.callbackName}.call(undefined,{ //👈👈注意!!! "success":true; "left":${newAmount}; }); `); response.end(); }
引伸義,那麼像這樣前端和後端交互,後端又傳入一個參數回調一個函數的技術,叫作JSONP。
前端代碼:
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>pay</title> </head> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> <body> <p>您的帳戶餘額: <span id="amount">$$amount$$</span></p> <button id='button'>付款1塊錢</button>> </body> <script> $('#button').on('click',function(){ var amount=document.getElementById('amount'); var image=document.createElement('script'); var functionName="philip"+parseInt(Math.random()*100000,10); 👈👈//隨機函數名 script.src='./pay?callback='+functionName; 👈👈//傳入隨機函數名 window[functionName]=function(status){ 👈👈//隨機函數名的函數 if(status === 'success'){ amount.innerText -= 1; }else if(){} } document.body.appendChild(script); script.onload=function(e){ // alert('success'); e.currentTarget.remove(); delete window[functionName]; } script.onerror=function(){ // alert('fail'); e.currentTarget.remove(); delete window[functionName]; } }) </script>
後端代碼:
if(path === './'){ var index_string = fs.readFileSync('./index.html','utf8'); var amount = fs.readFileSync('./db','utf8'); index_string.replace('$$amount$$',amount); response.setHeader('Content-type','text/html;charset=utf-8'); response.write(string); response.end(); }else if(path === '/pay'){ var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; fs.writeFileSync('./db',newAmount); response.setHeader('Content-type','application/javascript'); response.write(` ${query.callback}.call(undefined,'success'); 👈👈//修改成${query.callback} `); response.end(); }
$.ajax({ url: "", 👈👈//要請求的網址 dataType: "jsonp", 👈👈//我要使用jsonp success: function(status){ 👈👈//響應成功後,我要執行的函數 if(status === 'success'){ amount.innerText -= 1; } } })
jQuery幫咱們發送請求時傳入符合行業約定的參數:
jQuery幫咱們生成隨機函數名的回調函數:
Q:爲何JSONP不能使用POST請求?
A:一、JSONP是經過動態建立script實現的;二、動態建立script不能發送get請求
一、使用<form>
會刷新頁面
二、改用<img>
發送請求,但只能知道成功與否,沒有更多信息
三、改用<script>
四、如何獲得更多內容,添加?callback
參數
五、後端傳入一個參數,調用下callback
函數
六、OK,獲得內容,最終的方法三、四、5,就是JSONP了