面试题之Jsonp的理解及手写代码
分类:javascript
前言:
在前端面试过程中经常会被问到关于跨域的问题,跨域的解决方案。本篇文章就带大家从基础开始先理解,再到手写解决方案的代码,防止一看就会,一写就废。
什么是Jsonp?
- Jsonp(JSON with Padding) 是json的一种‘使用模式’,可以跨域的获取到数据。
- 了解同源机制;协议、域名、端口都相同,是一种安全策略,所有支持JavaScript的浏览器都使用这个策略。
- 跨域:当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
如何拿到网页中的json数据?
我们准备好一个网址photo.sina.cn/aj/index?pa… ,里面有json数据。现在要实现跨域拿到数据。
- xhr/fetch 因运行在 沙箱(Sandbox) 中的js同源机制,无法请求跨域sina 资源。在使用script 标签中有跨域访问资源能力有例如:
src link img a href.
沙箱:Sandbox是一种虚拟的程序运行环境,用以隔离可疑软件中的病毒或者对计算机有害的行为。比如浏览器就是一个Sandbox环境,它加载并执行远程的代码,但对其加以诸多限制,比如禁止跨域请求、不允许读写本地文件等等。
具体代码实现
<body>
<!-- 写一个callback函数 来得到数据 -->
<script>
function callback(data){
console.log(data);
}
</script>
<!-- 通过src 可以访问到外源网址 -->
<!-- 需要在网址后面加上 &callback=函数名 ;用一个函数来得到外源数据-->
<script src="https://photo.sina.cn/aj/index?page=1&cate=recommend&callback=callback"></script>
</body>
这样我们就可以通过callback()
函数来处理数据了
查看结果:
手写Jsonp函数
接下来我们希望使用Json原理封装一个Jsonp函数,不用再为了远程的资源。
代码思路:
- 准备带有padding的请求url,得到一个字符串dataStr
- 构造script
- js代码使用
document.createElement
创建一个script标签, - 在函数作用域中外界如何访问到?使用
window
初步版本:
<script>
let jsonp=(url,data={},callback='callback')=>{
//准备好带有padding的请求url
let dataStr=url.indexOf('?')=== -1?'?':'&'
// console.log(dataStr);
for(let key in data){
dataStr +=`${key}=${data[key]}&`
}
dataStr +=`callback=`+callback
//构造 script
let oScript=document.createElement('script')
oScript.src=url+dataStr
//appendChild () 方法可向节点的子节点列表的末尾添加新的子节点
document.body.appendChild(oScript)
window[callback]=(data)=>{
console.log(data);
}
}
jsonp('https://photo.sina.cn/aj/index?a=1',{
page:1,
cate:'recommend'
})
</script>
可以看一下实现效果,浏览器中是否以key value 的形式
打印出了数据
虽然实现了效果,但还可以在 window[callback]
这部分进行优化。
优化版本:
<script>
let jsonp=(url,data={},callback='callback')=>{
//准备好带有padding的请求url
let dataStr=url.indexOf('?')=== -1?'?':'&'
// console.log(dataStr);
for(let key in data){
dataStr +=`${key}=${data[key]}&`
}
dataStr +=`callback=`+callback
//构造 script
let oScript=document.createElement('script')
oScript.src=url+dataStr
//appendChild () 方法可向节点的子节点列表的末尾添加新的子节点
document.body.appendChild(oScript)
// window[callback]=(data)=>{
// console.log(data);
// }
return new Promise((resolve,reject)=>{
window[callback]=(data)=>{
try{
resolve(data)
}catch(e){
reject(e)
}finally{
oScript.parentNode.removeChild(oScript)// 注意这句代码,OScript移除,细节
}
}
})
}
jsonp('https://photo.sina.cn/aj/index?a=1',{
page:1,
cate:'recommend'
}).then(response=>{
console.log(response,'-------then');
})
</script>
服务器端的跨域访问
- 首先建立一个api-server文件夹,进行初始化:
npm init -y
- 新建index.js文件
var http = require('http');
http.createServer(function(req, res){
// req url callback=?
console.log(req.url); //结果: /?a=1&callback=callback
let data = {a: 1};
res.writeHead(200, {'Content-type' : 'text/json'})
const reg = /callback=([\w]+)/
if (reg.test(req.url)) {
let padding = RegExp.$1
res.end(`${padding}(${JSON.stringify(data)})`)
} else {
res.end(JSON.stringify(data));
}
// res.end('<p>Hello World</p>');
res.end(JSON.stringify(data));
}).listen(3000);
在之前手写的main.html的文件<script>
中添加以下代码:
jsonp('http://localhost:3000',{
a:1
})
.then(response =>{
console.log(response,'---------');
})
我们可以看一下结果:
- 我们通过服务器端跨域拿到了数据。
使用cors中间件实现跨域
首先安装cors:使用命令 yarn add cors
- 在server文件夹中新建一个
js
文件
var express = require('express');
var cors = require('cors');//后端cors 中间件
const app = express();
app.use(cors());
app.get('/product',(req,res)=>{
res.json({
a:1,
b:2
})
})
app.listen(8000,()=>{
console.log('server is ok')
})
- 在server文件夹外部新建一个html文件
<script>
fetch('http://localhost:8000/product')
.then(data=>data.json())
.then(data=>{
console.log(data)
})
</script>
实现结果:
总结:
我们知道了解决跨域的方案有:1. 借助src访问外网,使用callback=fun。2.封装Jsonp函数。3. 服务器端使用cors中间件。
本人是在校大三学生,也是在边学习边总结,如果有写的不妥的地方,请大家评论指出。