要了解什么是JSONP?本文从“历史由来”进行分析。
背景:一个页面需要与用户进行更好的交互,那就会涉及到访问数据的层面,不是简单的页面展示而已。数据这里指存在数据库文件中的数据,用户如何能在浏览的网页上看到数据库文件中的数据?这就需要浏览器(请求方)向服务器(响应方)发送请求,浏览器(前端)向服务器(后端)请求数据的方式有几种(本文需要你会用node简单的命令,懂点node相关知识,否则你可能需要多查一些资料弄清楚):
一、用form表单提交方案
常见的表单提交就是输入用户名,密码,至少必须有一个提交按钮才能提交填写的数据到服务器中。
提交的方法有两种,post和get,代码如下(mdn搬来的例子):
<!-- 一个简单的表单,这个表单会发送一个 GET 请求 -->
<form action="">
<label for="GET-name">Name:</label>
<input id="GET-name" type="text" name="name">
<input type="submit" value="Save">
</form>
<!-- 一个简单的表单,发送 POST 请求 -->
<form action="" method="post">
<label for="POST-name">Name:</label>
<input id="POST-name" type="text" name="name">
<input type="submit" value="Save">
</form>
用户点击提交按钮(type=submit)就可以提交数据给服务器,但这样的提交有个问题,每次提交后都会返回一个新页面,如果需求是提交后在本页不跳转,那继续看第二种img标签方案。(form表单太常见了不多说)
二、Img标签方案
代码:
本例子功能设计是点击“打钱”按钮,余额会减1,每次点击都减1。img请求方案是用js动态创建img标签,设置src为对应的服务器端可响应请求的地址,因为本例子网页存放在和服务器同一个域名下,所以省略了域名和端口直接为‘/pay’,如果服务器所绑定的域名为xxx.com,端口为9002,那么这里的src就设置为http://xxx.com:9002/pay。再.onload事件中,加入window.loacation.reload(),页面就会在加载图片成功的情况下刷新页面了,这里需要注意的是服务器会默认的要求我们必须真实的传递一张图片,否则请求会失败,解决方案是做个1*1px的透明图过去给服务器即可。
服务器server.js中处理来自/pay请求:(db为本例的数据库文件)
当发起请求的网址是/pay。就触发里面的代码,假设绑定的域名是xxx.com,那么打钱按钮在xxx.com/pay页面上,点击打钱按钮,服务器就会触发图中代码片段并响应,response.statusCode=200,为HTTP状态码,200表示请求成功,response.seHeader()设置请求头,这里设置请求类型为javascript,response.write()页面写入内容。看起来好像很好,但如例子所展示,img它只能知道成功和失败 不能知道更多的数据,那么就改用script标签方案。
三、script方案
同img标签一样,动态创建script标签,html代码:
服务器server.js代码:
点击打钱按钮时,你会发现每次点击,页面都会新加入一个script标签,这样必然不好,加上e.currentTarget.remove()移除即可:
图中的debugger为bug断点调试,删掉。用script标签不用写onload事件了,但好像也没有获取到任何服务器端的反馈信息,再进行改进:
<title>页面</title>
<h5>您的账户余额:<span id="amount">###amount###</span></h5>
<button id="btn">打钱</button>
<script>
window.yyy = function(result){
alert('这是前端的代码')
alert(`得到的结果是${result}`)
}
btn.addEventListener('click',(e)=>{
let script =document.createElement('script')
script.src='http://jack.com:9002/pay?callback=yyy'
document.body.appendChild(script)
script.οnlοad=function(e){
//移除对象
e.currentTarget.remove()
}
script.οnerrοr=()=>{
e.currentTarget.remove()
}
})
</script>
window.yyy是前端页面上为获取反馈信息的函数,result为接受服务器传递过来的参数,并把src改成服务器(jack.com:9002)地址:jack.com:9002/pya?callback='yyy',callback后面跟着函数名
服务器server:
else if(path === '/pay'){//路径为/pay时触发
var amout =fs.readFileSync('./db','utf8')
var newAmout =amount - 1
fs.wirteFileSync('./db',newAmount)
response.statusCode=200
response.write(`
yyy.call(undefined,'success')
`)
response.end()
}
代码中的success就是服务器传递给前端页面的参数(反馈信息),这里有个问题,后端服务器怎么知道前端写的反馈信息函数名为yyy???说明服务器那边的程序员要超级了解前端页面程序员写的代码,甚至谁他们是一个人,否则不能知道呀,不知道这服务器就不能做出响应了,这太耦合了(耦合通俗解释,两者之间联系太严格紧密了,一旦改变一点点就完!这个例子,只要服务器那边的函数名yyy写错成yyyy就凉了!),所以,我们要进行解耦(联系保持密但条件宽松了许多,就是变得灵活了,哈哈哈哈个人解释非官方),无论前端页面上写什么函数名,服务器那边都可以找到,所以再进行改动:
html改为(完整代码):
<title>页面</title>
<h5>您的账户余额:<span id="amount">###amount###</span></h5>
<button id="btn">打钱</button>
<script>
//设置随机函数名
let functionName= 'frank' + parseInt(Math.random()*10000,10)
window[functionName] = function(result){
alert('这是前端的代码')
alert(`得到的结果是${result}`)
}
btn.addEventListener('click',(e)=>{
let script =document.createElement('script')
script.src='http://jack.com:9002/pay?callback=' + functionName
document.body.appendChild(script)
script.οnlοad=function(e){
//移除对象
e.currentTarget.remove()
}
script.οnerrοr=()=>{
e.currentTarget.remove()
}
})
</script>
server.js(服务器)代码:
if(path === '/'){
// 获取html页面的内容 存放到string变量里
var string = fs.readFileSync('./index.html','utf8')
//读取db数据库文件内容,放如amount变量中
var amount =fs.readFileSync('./db','utf8')
// html页面中的的字符串###amount###替换成变量amount里的内容
string=string.replace('###amount###',amount)
response.statusCode = 200
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.statusCode = 200
// ${query.callback}指前端传过来的查询参数中的函数名
response.write(`
${query.callback}.call(undefined,'success')
`)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('呜呜呜')
response.end()
}
请求方(浏览器)创建script,src指向响应方(服务器)同时传递一个查询参数?callbackName=xxx,响应方根据查询参数callbackName,构造出形如: 函数名.call(undefined,’你要的数据’)的响应,浏览器接受到响应就会执行:函数名.call(undefined,’你要的数据’),那响应方就知道了他要的数据是什么,这样的数据访问方式就是JSONP,JSONP接口都是由后端提供。
因为,JSONP是通过动态创建script实现的 动态创建script的时候 只能用get 没有办法用post.
例子的完整项目代码可以在github上看:
https://github.com/wangxiaoniu/JSONP