最近回顾一下jsonp的请求方式,jsonp是一种比较老的前端跨域请求的方式,原理就是利用浏览器<script> <img>等标签src属性不受跨域限制,在script标签中写入方法获取接口数据,此跨域方案兼容性非常好,即便以前的老IE浏览器都可兼容。
第一步创建一个vue3项目,在src下创建一个jsonpSever.ts或js文件,文件内容如下
需要下载插件
npm install express
npm i body-parse
如果创建的是ts文件,建议下载 nodemon 热更新ts文件避免每次修改完ts文件手动重启问题
npm install --save-dev nodemon
此外ts文件还需要下载一个ts-node执行ts文件全局安装即可
npm install -g ts-node
在package.json添加一个执行命令实际根据知己的路径填写
"jsonp:dev": "nodemon src/jsonpSever/index.ts"
const express = require('express');
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json());
// 跨域配置
app.all('*', function (err: any, req: any, res: any, next: any) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Content-Type,Content-Length,Authorization,Origin,Accept,X-Requested-With'
);
res.setHeader('Content-type', 'application/json;charset=utf-8');
if (err.name === 'UnauthorizedError') res.status(401).send('invalid token...');
// ### next() 代表要此中间件执行完毕后执行下一个中间件, 一般在中间件里面书写, 其他地方不要用
next();
});
app.get('/bpmsever', (req: any, res: any) => {
// 这一步JSONP必备
const _callback = req.query.callback
// 服务端获取的callback就是我们定义的funapi
const response = {
code: 200,
message: 'success',
data: {
name: '测试'
}
}
if (_callback){
// 这两步设置发送也是NODE.JS发送JSONP必备
res.type('text/javascript')
res.send(_callback + `(${ JSON.stringify(response) })`)
} else {
res.json(response)
}
})
const port = 3030
app.listen(port, () => {
console.log(`server run at:\n- Local: http://localhost:${port}/`);
})
第二步封装api写入方法获取接口数据
/**
* @param {api} 请求路径
* @param {fnName} 获取数据的回调函数
* @param {f} 定义的获取数据的函数名
* @returns {Promise}
*/
function creatJsonpReq(api: string, fnName: Function, f: string) {
const body = document.getElementsByTagName('body')[0]
const javaScriptElApi = document.createElement('script')
const javaScriptElFn = document.createElement('script')
javaScriptElApi.type = 'text/javascript'
javaScriptElFn.type = 'text/javascript'
javaScriptElApi.src = api + '?' + `callback=${f}`
// javaScriptElFn.defer = true
const fn = fnName.toString() // append 允许传递参数 string | Node 需要转成字符串
javaScriptElFn.append(fn)
body?.append(javaScriptElApi)
body?.append(javaScriptElFn)
return new Promise((reslove) => {
fnName().then((res: any) => { reslove(res) })
})
}
第三步使用封装的方法
funcapi在vite-env.d.ts声明下,解决报window属性问题
interface Window { funcapi?: Function }
<script setup lang="ts">
import { onMounted } from 'vue'
import { creatJsonpReq } from '@/utils/index'
function jsonp(){
return new Promise((resolve, reject) => {
//在全局定义一个 doSomething 的函数
window.funcapi = function(data: any) { resolve(data) }
})
}
onMounted(async () => {
const data = await creatJsonpReq('http://localhost:3030/bpmsever', jsonp, 'funcapi')
console.log(data, 'data')
})
</script>
<style scoped>
</style>
执行"jsonp:dev": "nodemon src/jsonpSever/index.ts"命令后
再开一个终端执行vue文件最终可打印出data的值