1、JSONP是什么,解决了什么问题
JSONP 指的是 JSON with Padding。
由于跨域政策,从另一个域请求文件会引起问题。
从另一个域请求外部脚本没有这个问题。
JSONP 利用了这个优势,并使用 script 标签替代 XMLHttpRequest 对象。——w3c
Web页面上调用js文件时不受是否跨域的影响(不仅如此,我们还发现凡是拥有src
这个属性的标签都拥有跨域的能力,比如 、、)所以JSONP解决的问题是请求跨域的问题。如果还是不理解可以自行百度相关解释,这里不做重点解释,主要是看一下实现。
2、如何实现JSONP
了解了JSONP是什么,我们就来模拟一下一个JSONP请求。JSONP的实现方式一般是在请求参数里添加"callback=函数名"的方式,这里我们使用nodejs作为服务。
首先搭建一个本地服务,这里使用express,先全局安装一下express
npm install express -g
然后创建一个文件夹,进入文件夹目录下执行下面命令
npm init
这里一路回车就可以
安装express
npm install express -s
这个时候我们的目录是这样的
├── node_modules
├── package-lock.json
└── package.json
在根目录下新建index.js文件
├── index.js
├── node_modules
├── package-lock.json
└── package.json
文件内容如下
var express = require('express')
//2. 创建express服务器
var server = express()
//3. 访问服务器(get或者post)
//参数一: 请求根路径
//3.1 get请求
server.get('/', function(request, response) {
// console.log(request)
response.send({ name: 'starfishing' })
})
server.get('/json/getUser', function(request, response) {
console.log(request.query)
let callback = request.query.callback
// console.log(request)
let str = callback + "({'name':'starfishing','age':22})"
response.send(str)
})
//4. 绑定端口
server.listen(4040)
console.log('启动4040')
到这里我们的服务就搭好了,执行node index.js启动服务
然后新建一个html页面,内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSONP测试</title>
</head>
<body>
<script>
function jsonp(data){
// 这是我们的回调函数
console.log(data.name,data.age)
}
</script>
<script src="http://localhost:4040/json/getUser/?callback=jsonp"></script>
</body>
</html>
在浏览器打开页面,调出控制台,可以看到我们回调函数打印出的内容
3、jsonp进阶,使用promise实现jsonp
我们会基于promise封装一个JSONP函数,包括请求超时逻辑,下面贴一下代码
function JSONP(url, params, overtimes) {
return new Promise(function(resolve, reject) {
let query = []
//注册回调函数
let callbackName = 'jsonp' + Math.ceil(Math.random() * 1000000)
window[callbackName] = function(json) {
head.removeChild(script) //移除scipt标签
script.timer ? clearTimeout(script.timer) : false //清除超时计时器
window[callbackName] = null
resolve(json) //成功处理
}
for (let key in params) {
query.push(key + '=' + params[key])
}
query.push('callback=' + callbackName)
let remote = url + '/?' + query.join('&')
let script = document.createElement('script')
script.src = remote
let head = document.getElementsByTagName('head')[0]
head.appendChild(script)
script.onerror = function(err) {
if (window && window[callbackName]) {
delete window[callbackName]
}
try {
head.removeChild(script)
} catch {}
reject(err)
}
if (overtimes) {
script.timer = setTimeout(function() {
if (window && window[callbackName]) {
delete window[callbackName]
}
try {
head.removeChild(script)
} catch {}
reject({ timer: 'overtime' })
}, overtimes)
}
})
}
然后对我们的node服务做一下改造来验证我们的函数
var express = require('express')
//2. 创建express服务器
var server = express()
//3. 访问服务器(get或者post)
//参数一: 请求根路径
//3.1 get请求
server.get('/', function(request, response) {
// console.log(request)
response.send({ name: 'xiaoxin' })
})
server.get('/json/getUser', function(request, response) {
console.log(request.query)
let callback = request.query.callback
let str = callback + "({'name':'starfishing','age':22})"
// 下面是验证正常返回的情况
response.send(str)
// 下面是验证服务器错误的情况
// response.status(500)
// 下面验证超时
// setTimeout(function() {
// response.send(str)
// }, 6000)
})
//4. 绑定端口
server.listen(4040)
console.log('启动4040')
最后把新的函数放到HTML文件中进行验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSONP测试</title>
</head>
<body>
<script>
function JSONP(url, params, overtimes) {
// 这里就是我们上面封装的函数,直接拷贝到这里,避免篇幅太长,这里就不拷进来了
}
</script>
<script>
JSONP('http://localhost:4040/json/getUser', { name: 'haha' }, 5000)
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
</script>
</body>
</html>