寫一個(gè)JSONP

首先讓我們來先寫一個(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


ASYUJJ.png

當(dāng)我們點(diǎn)擊一下余額數(shù)字就會(huì)減一,但是如果刷新頁面烛愧,數(shù)字就會(huì)重新回到100,所以我需要一個(gè)數(shù)據(jù)庫來存放余額


ASYhQI.png

我們給余額添加一個(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>
ASwkss.png

但是這樣體驗(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


ASBHRU.png

我們可以把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()
  }
})
ASs6G6.png

jack.com操作它的數(shù)據(jù)庫打錢


ASy9J0.png

但是這里有一個(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()
  }
})
AS6sgK.png

我們把回調(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末深浮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子眠冈,更是在濱河造成了極大的恐慌飞苇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜗顽,死亡現(xiàn)場離奇詭異布卡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)雇盖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門忿等,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人崔挖,你說我怎么就攤上這事贸街。” “怎么了狸相?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵薛匪,是天一觀的道長。 經(jīng)常有香客問我脓鹃,道長逸尖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任瘸右,我火速辦了婚禮娇跟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘太颤。我一直安慰自己苞俘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布龄章。 她就那樣靜靜地躺著吃谣,像睡著了一般乞封。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上基协,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音菇用,去河邊找鬼澜驮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惋鸥,可吹牛的內(nèi)容都是我干的杂穷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼卦绣,長吁一口氣:“原來是場噩夢啊……” “哼耐量!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滤港,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤廊蜒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溅漾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體山叮,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年添履,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屁倔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暮胧,死狀恐怖锐借,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情往衷,我是刑警寧澤效诅,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站袄膏,受9級特大地震影響闻牡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俺亮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一驮捍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脚曾,春花似錦东且、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鲁冯。三九已至,卻和暖如春色查,著一層夾襖步出監(jiān)牢的瞬間薯演,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工秧了, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跨扮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓验毡,卻偏偏與公主長得像衡创,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子晶通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5璃氢? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 27,422評論 1 45
  • 1. JSONP的實(shí)現(xiàn)原理 JSONP是通過動(dòng)態(tài)創(chuàng)建script實(shí)現(xiàn)的狮辽。請求方:frank.com 的前端程序員(...
    王童孟閱讀 468評論 0 2
  • 什么是跨域 跨域一也,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的喉脖,是瀏覽器對JavaScript實(shí)...
    Yaoxue9閱讀 1,286評論 0 6
  • 什么是跨域 跨域塘秦,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的动看,是瀏覽器對JavaScript實(shí)...
    他方l閱讀 1,059評論 0 2
  • 簡單的前后端交互 前面學(xué)習(xí)了這么多尊剔,都是在和頁面打交道,不管是HTML菱皆、CSS须误、JavaScript,DOM仇轻,都沒...
    astak3閱讀 275評論 0 0