什么是jsonp 从零开始构建 1 2 3 4 mkdir jsonp_demo //创建目录 jsonp_demo cd jsonp_demo //切换目录到 json_demo touch server001.js touch index001.html
版本一
server001.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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 /******** 从这里开始看,上面不要看 ************/ console.log('被请求后:含查询字符串的路径\n' + pathWithQuery) if(path=='/'){ var string = fs.readFileSync('./index001.html','utf8'); response.setHeader('Content-Type','text/html;charset=utf-8'); response.write(string); response.end(); }else{ response.statusCode = 404; response.end(); } /******** 代码结束,下面不要看 ************/ }) server.listen(port) console.log('监听 ' + port + ' 成功\n请在浏览器输入 http://localhost:' + port)
当前目录下 新建index.html main.js style.css (main.js/style.css内容为空暂时不需要)
index.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="./style.css"> <title>Document</title> </head> <body> <h5>您的账户余额是 <span id="amount">100</span> </h5> <button id="btn">付款1块钱</button> <script src="./main.js"></script> <script> btn.addEventListener('click',()=>{ let n = amount.innerText; let number = parseInt(n,10); let newNumber = number - 1; amount.innerText = newNumber; }) </script> </body> </html>
运行 server001.js 所在目录 打开命令行运行 1 2 3 4 node server001.js 9999 //你会看到 监听 9999 成功 请在浏览器输入 http://localhost:9999
打开浏览器输入 http://localhost:9999
点击按钮后你会看到 余额变动如下
问题1 每次点击按钮都会把余额减1 但是浏览器只要刷新就会重新显示100
如何长久的保存用户的数据呢?不可能你的余额永远是100吧 那岂不是有花不完的钱
长久的保存用户数据 保存到文件里
文件系统 将数据保存到服务器的文件里 这个文件专门为这个用户服务1 2 3 touch db //创建 db 文件 vi db //打开之后写入100
版本二 修改html里写死的100 换用一个‘匹配’字符替换 ==> “&&&amount&&&”
index002.html1 2 3 4 5 //将这一行 <h5>您的账户余额是 <span id="amount">100</span> </h5> //修改为 //自己定义替换的匹配字符串 "&&&amount&&&" <h5>您的账户余额是 <span id="amount"> &&&amount&&&</span> </h5>
server002.js 修改如下内容 从db读取 用户余额1 2 3 4 5 6 7 8 9 10 if(path=='/'){ var string = fs.readFileSync('./index002.html','utf8'); //读取db文件里用户余额 var amount = fs.readFileSync('./db','utf8'); //100 //替换 index002.html 里定义的 匹配字符 为 用户余额 string = string.replace('&&&amount&&&',amount); response.setHeader('Content-Type','text/html;charset=utf-8'); response.write(string); response.end(); }
这样做了之后你会发现 一刷新 数据还是100
解决长久保存
我们点击付款的时候 告诉服务器 请把数据库里的余额给我
所以我们点击付款的时候不能单独修改本地了 要去给服务器发 post请求
为什么不是get 付款啊!这么重要的操作你敢用get?
版本三 index003.html 1 2 3 4 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <form action="/pay" method="post"> <input type="submit" value="付款1块钱"> </form>
server003.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if(path=='/'){ var string = fs.readFileSync('./index003.html','utf8'); var amount = fs.readFileSync('./db','utf8'); //100 string = string.replace('&&&amount&&&',amount); response.setHeader('Content-Type','text/html;charset=utf-8'); response.write(string); response.end(); }else if(path=='/pay'&& method.toUpperCase() === 'POST'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 response.write('success'); response.end(); }else{ response.statusCode = 404; response.end(); }
再去开启你的服务器 1 2 node server003.js 9999 //在浏览器打开 http://localhost:9999
http://localhost:9999
你去打开db文件 发现余额改变了
以上就是最最简单、最最原始的数据库的展示 再次修改一下server003.js里的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 else if(path=='/pay'&& method.toUpperCase() === 'POST'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 if(Math.random()>0.5){ fs.writeFileSync('./db',newAmount); //更新余额 response.write('success'); }else{ response.write('fail'); } response.end(); }
以上是:旧时代的前后端配合
操作数据库成功返回success 失败返回fail
在一个2018年的今天
用户怎么可能忍受
点击付款
进入成功/失败页面
点击返回
点击刷新才看到你最新的数据
古老方式-优化一下用户体验
用iframe承载这个form的提交 后的 success/fail响应 但是I现在已经没人用啦!
版本四 index004.html 1 2 3 4 5 6 7 <body> <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <form action="/pay" method="post" target="result"> <input type="submit" value="付款1块钱"> </form> <iframe name="result" src="about:blank" frameborder="0"></iframe> </body>
版本五 index005.html 1 2 3 4 5 6 7 8 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let img = document.createElement('img'); img.src = '/pay'; }) </script>
点击按钮的时候 会创建一个img标签 设置它的 src = ‘你的请求路径’ 就会触发请求
这样的缺陷就是 它无法post 它只能get 问题来了? 我们如何知道它成功or失败呢? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let img = document.createElement('img'); img.src = '/pay'; //我们如何知道请求成功or失败呢? img.onload = function(){ alert('打钱成功') //我们帮用户刷新 但是会重绘整个页面 //window.location.reload(); //因为知道固定减1 所以我帮用户减1 amount.innerText = amount.innerText - 1; } img.onerror = function(){ alert('打钱失败') } }) </script>
server005.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 if(Math.random()>0.5){ fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','image/png'); //设置状态码 response.statusCode = 200; //返回响应的图片 不然 会走入img.onerror 事件里 response.write(fs.readFileSync('./dog.jpg')); }else{ response.statusCode = 400; response.write('fail'); } response.end(); }
这样我们就完成了 无刷新的发送请求 俗称 image发请求法 改良 script发请求法
注意动态创建script 同时设置src 必须把script添加到body里才会生效
版本六 server006.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 /******** 从这里开始看,上面不要看 ************/ console.log('被请求后:含查询字符串的路径\n' + pathWithQuery) if(path=='/'){ var string = fs.readFileSync('./index006.html','utf8'); var amount = fs.readFileSync('./db','utf8'); //100 string = string.replace('&&&amount&&&',amount); response.setHeader('Content-Type','text/html;charset=utf-8'); response.write(string); response.end(); }else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 if(Math.random()>0.5){ fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write(''); }else{ response.statusCode = 400; response.write('fail'); } response.end(); }else{ response.statusCode = 404; response.end(); } /******** 代码结束,下面不要看 ************/
index006.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let script = document.createElement('script'); script.src = '/pay'; document.body.appendChild(script); //我们如何知道请求成功or失败呢? script.onload = function(){ alert('打钱成功') } script.onerror = function(){ alert('打钱失败') } }) </script>
这样每次点击会生成一个script
我们知道 script里的代码是会执行的
所以我们就不需要在 server006.js里写失败的情况 它会自动进入 script.onerror里
step1修改server006.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write('alert("pay")'); response.end(); }
运行发现如果成功了 就会 弹出pay 然后再弹出‘打钱成功’
不用监听script.onload了
因为 成功自然会在server006.js 里 写入并执行 response.write(‘alert(“pay”)’);
版本七 index007.html 1 2 3 4 5 6 7 8 9 10 11 12 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let script = document.createElement('script'); script.src = '/pay'; document.body.appendChild(script); script.onerror = function(){ alert('打钱失败') } }) </script>
server007.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write( ` amount.innerText = amount.innerText - 1; ` ); response.end();
优化上面运行后每次点击按钮页面都会多一个script标签
成功或者失败后干掉script
版本八 index008.html
注意:server里文件路径
所以我们还是得监听onload事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let script = document.createElement('script'); script.src = '/pay'; document.body.appendChild(script); script.onload = function(e){ alert('打钱成功') e.currentTarget.remove(); } script.onerror = function(e){ alert('打钱失败') e.currentTarget.remove(); } }) </script>
注意:不管成功还是失败script都会移除 但是script还在内存里只是从页面移除了
这个方案就叫 SRJ (Server rendered javascript)服务器返回的 javascript
无刷新局部更新页面内容的方案 在ajax之前
script是不受域名限制的 这跟跨域没关系
aa.com:9999的前端 是可以访问 bb.com:8888的后台接口的 这就是SRJ
JSONP 查看后端代码 发现– 后端要对前端代码非常了解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write( ` amount.innerText = amount.innerText - 1; ` ); response.end();
bb.com:8888的后端居然要对 aa.com:9999的页面如此清楚
耦合 解耦 版本九 index009.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> //某域名下的前端 bb.com:9999的前端 window.xxx = function(result){ alert(`得到了结果是${result}`); } btn.addEventListener('click',function(){ let script = document.createElement('script'); script.src = '/pay'; document.body.appendChild(script); script.onload = function(e){ e.currentTarget.remove(); } script.onerror = function(e){ alert('打钱失败') e.currentTarget.remove(); } }) </script>
server009.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write( ` xxx.call(undefined,'success') //执行xxx方法并返回 字符串 ` ); response.end(); }
这样的好处是什么
后端不需要知道任何细节,只调用xxx.call()返回结果
这样两个网站之间就可以无缝的沟通
xxx.call()是不是很丑?
于是传递一个参数callback=xxx
callback =’你自己定义的方法名’
版本十 index010.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> //某域名下的前端 bb.com:9999的前端 window.yyy = function(result){ alert(`得到了结果是${result}`); } btn.addEventListener('click',function(){ let script = document.createElement('script'); //传递自己定义的方法名 script.src = '/pay?callback=yyy'; document.body.appendChild(script); script.onload = function(e){ e.currentTarget.remove(); } script.onerror = function(e){ alert('打钱失败') e.currentTarget.remove(); } }) </script>
server010.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write( ` ${query.callback}.call(undefined,'success') ` ); response.end(); }
JSONP解决的问题是什么 两个网站之间如何交流
因为script是不受域名限制的 ajax是受域名限制的
由于不受域名限制,我就告诉对方的网站 我要请求一个数据,
请你把数据给我
你给我之后调用我的xxx
然后把参数给我 xxx.call(undefined,参数)
传递的参数不仅仅是字符串还可以是json 1 2 3 4 5 6 7 8 response.write( ` ${query.callback}.call(undefined,{ "success":true, "data":99 }) ` );
json1 2 3 4 { "success":true, "data":99 }
json的左边是 左padding : ${query.callback}.call(undefined,
json的右边是 右padding : )
于是乎 JSON + padding = JSONP
版本十一 index011.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script> btn.addEventListener('click',function(){ let script = document.createElement('script'); //生成无重复的方法名 let fnName = 'hjx'+parseInt(Math.random()*100000,10); window[fnName] = function(result){ //根据接收的json结果做处理 if(result === 'success'){ amount.innerText = amount.innerText - 1; } } //传递自己定义的方法名 script.src = '/pay?callback=' + fnName; document.body.appendChild(script); script.onload = function(e){ e.currentTarget.remove(); //不管成功失败都要删除生成的fnName属性 delete window[fnName]; } script.onerror = function(e){ alert('打钱失败') e.currentTarget.remove(); //不管成功失败都要删除生成的fnName属性 delete window[fnName]; } }) </script>
server011.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 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 /******** 从这里开始看,上面不要看 ************/ console.log('被请求后:含查询字符串的路径\n' + pathWithQuery) if(path=='/'){ var string = fs.readFileSync('./index011.html','utf8'); var amount = fs.readFileSync('./db','utf8'); //100 string = string.replace('&&&amount&&&',amount); 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/css'); response.write(string); response.end(); }else if(path=='/main.js'){ var string = fs.readFileSync('./main.js','utf8'); response.setHeader('Content-Type','text/javascript'); response.write(string); response.end(); }else if(path=='/pay'){ //读取 db中用户余额 var amount = fs.readFileSync('./db','utf8'); var newAmount = amount - 1; //余额减1 fs.writeFileSync('./db',newAmount); //更新余额 //设置响应头 response.setHeader('Content-Type','application/javascript'); //设置状态码 response.statusCode = 200; //返回响应内容 response.write( ` ${query.callback}.call(undefined,'success') ` ); response.end(); }else{ response.statusCode = 404; response.end(); } /******** 代码结束,下面不要看 ************/ }) server.listen(port) console.log('监听 ' + port + ' 成功\n请在浏览器输入 http://localhost:' + port)
写了那么多 其实你可以使用jQuery 几句话就能搞定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5> <button id="btn">打钱</button> <script src="//code.jquery.com/jquery-1.9.1.min.js"></script> <script> btn.addEventListener('click',function(){ $.ajax({ url:"http://localhost:9999/pay", dataType:"jsonp", success:function(res){ console.log(res); } }) }) </script>
注意: $.ajax()只是把jsonp归纳到 jq里 而它不是ajax 考点1 :请问JSONP为什么不支持POST请求
因为JSONP是通过动态创建script实现的
我们动态创建script的时候只能用get 没有办法用post
考点2 : 什么是 JSONP?
1请求方创建一个script标签src指向响应方,并传递callback =回调函数名
2响应方根据callback查询参数构造一个 xxx.call(undefined,传递的内容)
3浏览器接到响应,就会执行 xxx.call(undefined,传递的内容)
4请求方得到他想要的数据