你真的懂JSONP嗎?
JSONP是什么,在谷歌里面搜索JSONP,可以从维基百科里面得到它的定义:
JSONP(JSON with Padding)是数据格式
创建一个iframe标签,然后把form表单的target指向iframe的name,这样,每次在提交请求的时候,就不会刷新本页面了,而是刷新iframe里的页面,但是很明显的,这是一种相当不理想的办法,原因是页面有一个这样专门用来防止刷新的iframe很碍眼。
2.所以就有了第二种方法,第二种方法就是:不用form表单提交请求。
于是我们果断地,舍弃了form表单这种方法~
那么,除了用form表单来发送请求,我们还有以下几种方法可以实现发送请求的功能:
1.用a标签也可以发送get请求,但其问题和form表单一样,就是会刷新页面或新开页面。
2.用img可以发送get请求,但是它的问题是得到的数据只能以图片的形式来展示。显然很多场景下这种办法并不符合需求。
3.用 link 可以发送get请求,但是只能以CSS、favicon的形式展示。
4.用 script 可以发送get请求,这种方法又称为SRJ方案,S为Server,R为Rendered,J为JavaScript。值得在这里说明的是,script标签是只能发送get请求的。
具体实现的JS部分代码如下:<body> <p style="color:red;">您的余额是<span id=amount>&&&amount&&&</span></p> <button id=button>付款</button> <script> $('#button').on('click',function(){ let script=document.createElement('script') //创建script标签 script.src='pay' //scipt标签的请求路径 document.body.appendChild(script) script.=function(e){ e.currentTarget.remove() } //当script标签加载完毕之后,删除script标签 script.=function(e){ alert('fail'); e.currentTarget.remove() }//当script标签请求失败之后,通知用户请求失败,并删除script标签 }) </script></body>服务器端部分代码:
if(path === '/'){ let string=fs.readFileSync('./index.html','utf8') let amount=fs.readFileSync('./db','utf8') 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'){ let amount=fs.readFileSync('./db','utf8') amount-- fs.writeFileSync('./db',amount) //改变数据库内账户余额数量 response.statusCode = 200 response.setHeader('Content-Type', 'application/javascript;charset=utf-8') response.write(` amount.innerText-- `) //返回的script标签里的内容,返回后会执行,且比事件先执行 response.end() }这里需要说明的是,db为通过文件系统自己创建的数据库,里面存有的是当前余额的数量。
看到这里,或许你会觉得,咦,好像需求得到解决了,现在的方案听完善了吖感觉~
是这样吗?NO!
以上代码有什么问题呢,问题在于,后端程序员需要对前端的页面细节了得地十分清楚,存在着一个耦合的问题,为什么这么说呢,请看服务器端的代码,在response.write()
这里,响应的内容是前端页面的代码细节,这就要求后端程序员对前端程序员所写的页面代码十分了解,这显然是不符合实际的。
那要怎么改善呢?很简单,只要我们不返回细节,返回函数名来表示成功即可,然后在前端页面上定义这个成功时候执行的函数就可以了,具体服务器端的部分代码如下所示:response.write(` callback.call(undefined,'success') `)然后在前端页面中新建一个成功时候执行的callback函数:
window.callback=function(result) { alert('这里是前端代码') alert(`我得到的结果是${result}`) }
这里还有一个问题,那是不是后端程序员需要知道每个成功函数的函数名呢,其实并不需要,只要我们把这个函数名提交给服务器端就好了,所以下面我们进一步对其进行改进。
以下为服务器端的部分代码:response.setHeader('Content-Type', 'application/json;charset=utf-8') response.write(` ${query.callback}.call(undefined,{ "success":true, "left":${amount} }) `)前端页面的部分代码:
<body> <p style="color:red;">您的余额是<span id=amount>&&&amount&&&</span></p> <button id=button>付款</button> <script> $('#button').on('click',function(){ let script=document.createElement('script') script.src='/pay?callback=yyy' //函数名yyy作为查询参数被提交给服务器 document.body.appendChild(script) script.=function(e){ e.currentTarget.remove() } script.=function(e){ alert('fail'); e.currentTarget.remove() } }) window.yyy=function(result){ amount.innerText=result.left } </script></body>上面代码中,函数名yyy作为查询参数被提交给服务器,然后服务器端通过
${query.callback}
获取到了这个函数名,然后把调用这个函数名的代码作为响应发送回给客户端也就是前端页面,有人可能会发现,响应头response.setHeader
中出现了改变,响应类型从javascript
变成了json
,没错,上面代码返回了一个JSON格式的代码片段:{ "success":true, "left":${amount}}JSON两边的代码称为Padding,因此JSON+Padding=JSONP,这个就是JSONP这个名字的由来。
JSON是一种语言,这种语言的详细语法就不在这里介绍了,大家可以通过在其注意注意:上述代码,和AJAX没有半毛钱关系!
另外,再次强调,script标签是只能发送get请求的,因此JSONP也只能实现跨域发送get请求。
以后当再有面试官问到你:请问JSONP为什么不支持post请求的呢?
你可以拍拍胸膛告诉他:
1.因为JSONP是通过动态创建script实现的。
2.我们动态创建script的时候只能使用get,没有办法使用post。这样的回答,满分!~
作者:宣泽彬
链接:https://www.jianshu.com/p/771181b1e437
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章