先看百度解析如下:
JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”,可以让网页从别的网域要数据,利用script元素开放策略,网页可以从其他来源动态产生的JSON数据,而这种使用模式就是所谓的 JSONP。
了解什么是数据库?
1、文件系统是一个数据库
2、MySQL是一个数据库
只要能长久的存数据,就是数据库。
下面要讲一段故事:
<h5>您的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="POST">
<input type="submit" value="付款">
</form>
<script src="/server.js"></script>
action="/pay" method="post"前端写这些参数,让后端知道如何操作。
创建一个db(随便打的)的文件,作为数据库,里面就写100。
后台关联起来:
if(path === '/'){ //如果用户请求的路径是根目录
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./db','utf8') //读取这两个文件里的数据
string = string .replace('&&&amount&&&',amount)
//把db数据库文件里的内容替换进文中&&&amount&&&处
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/server.js'){
var string = fs.readFileSync('./server.js','utf8')
response.setHeader('Content-Type', 'application/javascrip')
response.write(string)
response.end()
}else if(path === '/pay' && method.toUpperCase() === 'POST'){
//如果路径是pay,且发送的是post
var amount = fs.readFileSync('./db','utf8') //100
var newAmount = amount - 1
fs.writeFileSync('./db',newAmount)
response.write('success')
response.end()
}else{
response.statusCode = 400
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write()
response.end()
}
至此这就是旧时代的前后端配合交互的方法。
以下说说问题,使用form发请求,每次点击付款,成功-1后要返回并刷新页面,才能更新数据。虽然我们可以加个<iframe name="result" src="about:blank" frameborder="0" height="200"></iframe>,但还是需要刷新,用户体验不良好。
后来人们发现不止是form,a,img,link,script标签,都可以发送请求。
那么试试用创建img来发送请求:
<h5>您的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click',(e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打钱成功')
window.location.reload()
}
image.onerror = function(){
alert('打钱失败')
}
})
</script>
发现img有个缺陷,无法发送post请求,因为除了能写image.src = '/pay'外,浏览器没有给选项能选post,所以他只能get。虽然无论成功或失败,都会弹出提示,但是最后还是需要用户手动刷新页面更新数据,用户体验不良好。
所以为了用户体验,加个代码window.location.reload()成功后自动帮用户刷新页面,这时用户体验就非常良好了。但是又有个问题,简单的页面或许可以,但是若页面内容多了,这样每次都刷新页面就会重新渲染整个页面,当然是不太好吧?那还有什么更好的呢?
既然如此,我们来帮用户-1,会怎么样呢:
<h5>您的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click',(e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打钱成功')
amount.innerText=amount.innerText - 1
}
image.onerror = function(){
alert('打钱失败')
}
})
</script>
后台:
if(path === '/'){ //如果用户请求的路径是根目录
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./db','utf8') //读取这两个文件里的内容
string = string .replace('&&&amount&&&',amount) //把db数据库文件里的内容替换进文中&&&amount&&&处
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/server.js'){
var string = fs.readFileSync('./server.js','utf8')
response.setHeader('Content-Type', 'application/javascrip')
response.write(string)
response.end()
}else if(path === '/pay'){ //如果路径是pay
var amount = fs.readFileSync('./db','utf8') //100
var newAmount = amount - 1
fs.writeFileSync('./db',newAmount)
response.setHeader('Content-Type', 'image/png')
response.write(fs.readFileSync('./a.png'))
response.end()
}else{
response.statusCode = 400
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应路径,你需要自行修改index.js')
response.end()
}
运行,点击打钱,弹出提示成功,数字自动-1,不用刷新页面。只在数字那里-1,这叫局部刷新。
现在看看script方法:
<script>
button.addEventListener('click',(e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('打钱成功')
}
script.onerror = function(){
alert('打钱失败')
}
})
</script>
//以上省略。。。
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8') //100
var newAmount = amount - 1
fs.writeFileSync('./db',newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.write('alert("success")')
response.end()
}
注意:script也没有post接口
首先好处是不用返回图片,返回字符串速度会更快。但是这个script是会执行的呀!每点击一次,会不自觉多出一个script,这个有pay的script也是会执行的,先执行完后台弹出提示,再执行onload事件,既如此,干嘛要多执行一个。删掉删掉~
顺便把多出来的script解决一下:
<script>
button.addEventListener('click',(e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(){
alert('打钱失败')
e.currentTarget.remove()
}
})
</script>
......
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8') //100
var newAmount = amount - 1
fs.writeFileSync('./db',newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.write(`
amount.innerText = amount.innerText - 1`)
//这段代码为什么会被当做js执行,是因为它基于http协议,上面就声明了是js代码,而且我们以script引入
response.end()
}
删掉提示框,删掉会增多的script(虽然删掉了但是变量还在内存里的,只不过在页面看不见了而已~),页面数据其实还是需要用手动刷新来更新的,但我们选择帮用户自动-1。局部刷新~纵享丝滑
以上方法也叫SRJ(Server randered Javascript),直译:服务器返回的js
是一种无刷新局部更新页面内容方案
script可以不受域名限制访问任何网页,除非对方有防盗链。
这里有一件重大提示:get极其容易伪造,所以重要操作如打钱取钱pay付只会用post来做。
好了,现在我们来回想一下细节:
callback.call(),无论你发来什么,我一律给你返回去就行了。
也许后端给你返json语言呢?
这就是JSON + Padding = JSONP 它在某些情况下可以是JSONP
但大部分情况下其实就是String + Padding = StringP 利用动态标签进行跨域请求。
看一眼完整代码:
<script>
button.addEventListener('click',(e)=>{
let script = document.createElement('script')
let functionName = 'abcds'+parseInt(Math.random()*100000,10)
//十进制随机正整数,形如:abcds647379779734327 的随机函数名
window[functionName] = function(result){
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else{}
}
script.src = '/pay?callback='+functionName
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
delete window[functionName] //每次用完函数名,删掉,每一次就会是生成新的名字,不会重复,这样也不会污染全局变量
}
script.onerror = function(){
alert('打钱失败')
e.currentTarget.remove()
delete window[functionName]
}
})
</script>
这里添加了随机函数名,每次执行,都会生成一个随机正整数函数名,callback传参给服务器,服务器执行完后返回对应函数,并且把生成的随机名删掉,确保每次执行函数名都不一样(可以理解为一次性名字)。
jQuery版:
<script>
button.addEventListener('click', (e) => {
$.ajax({
url: "/pay",
dataType: "jsonp",
success: function (response) {
if (response === 'success') {
amount.innerText = amount.innerText - 1
}
}
})
})
</script>
jquery只需要提供url,使用什么类型,成功之后要做什么就行了
总结:
用form可以发送get,post请求,但需要刷新页面或新开一个页面
用a可以发送get请求,但需要刷新页面或新开一个页面
用img可以发送get请求,但只能以图片形式展示
用link可以发get请求,但只能以css,favicon的形式展示
用script可以发get请求,但只能以脚本形式运行
JSONP
1、请求方创建script,src指向响应方,同时传一个查询参数 ?callback=xxx
2、响应方根据查询参数callbackname,构造形如:
1、xxx.call(undefined,'你要的参数')
2、xxx('你要的数据')
3、浏览器接受到响应就会执行xxx.call(undefined,'你要的参数')
4、那么请求方就知道了他要的数据
这就是JSONP
题外话:为什么JSONP不支持POST请求?
因为JSONP是通过动态创建script实现的,动态创建script时只能用get,无法用post。
form表单会刷新页面,img只能知成功or失败,不能知更多数据,用script是为了让后台知道我用的是代码,callback传参告诉他。