一 目录
不折腾的前端,和咸鱼有什么区别
目录 |
---|
一 目录 |
二 前言 |
三 根本实现 |
3.1 步骤一:前端局部 |
3.2 步骤二:后端局部 |
3.3 步骤三:前端局部 |
四 手写 JSONP |
4.1 封装 JSONP |
4.2 简略实现 |
4.3 欠缺版本 |
五 参考文献 |
二 前言
返回目录
基本原理 :利用 script
标签的 src
没有跨域限度来实现跨域目标。
执行过程 :
- 前端定义一个解析函数:
jsonpCallBack = function (res) {}
- 通过
params
的模式包装script
标签的申请参数,并且申明执行函数,如:cb = jsonpCallBack
- 后端获取前端申明的执行函数(
jsonpCallBack
),并且带上参数且调用函数的形式传递给前端 - 前端再
script
标签返回资源的时候就会执行jsonpCallBack
并通过回调函数的形式拿到数据。
优缺点 :
- 【毛病】只能进行
GET
申请 - 【长处】兼容性好,在一些古老的浏览器中都能够运行。
注:本篇文章绝大部分参考 LinDaiDai_霖呆呆 大佬的手写内容,基于集体了解进行的再创作。呆呆大佬的文章在参考文献已给出
三 根本实现
返回目录
上面咱们通过 3 步骤来理解如何应用 JSONP
。
3.1 步骤一:前端局部
返回目录
前端代码:
index.html
<script type='text/javascript'>
window.jsonpCallBack = function(res) {console.log(res);
}
</script>
<script src='http://localhost:8080/api/jsonp?id=1&cb=jsonpCallBack' type='text/javascript'></script>
- 创立一个
jsonpCallBack
函数,然而还没有被调用。 - 加载
src
中的资源,调用localhost:8080
端口的 API:api/jsonp
,传递的参数是id = 1
以及cb = jsonpCallBack
3.2 步骤二:后端局部
返回目录
后端代码:
const Koa = require('koa');
const app = new Koa();
const items = [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}]
app.use(async (ctx, next) => {if (ctx.path === '/api/jsonp') {const { cb, id} = ctx.query;
const title = items.find(item => item.id == id)['title']
ctx.body = `${cb}(${JSON.stringify({title})})`;
return;
}
})
console.log('listen 8080...')
app.listen(8080);
- 解析
ctx.query
,将id
和cb
获取到。 - 查找
title
- 返回一个字符串,该字符串调用
cb
办法,并将title
转成字符串,返回到内容ctx.body
中。
3.3 步骤三:前端局部
返回目录
这时候前端接管到 Node.js 的返回,间接调用了 cb(title)
,办法,最终执行了它的回调,从而执行:
window.jsonpCallBack = function(res) {console.log(res);
}
输入:{title: 'title1'}
四 手写 JSONP
返回目录
4.1 封装 JSONP
返回目录
定义一个 JSONP
办法,接管 4 个参数:
url
:api
接口params
:api
接口参数callbackKey
:与后端约定回调函数用哪个字段callback
:拿到数据之后前端执行的回调函数
JSONP({
url: 'https://www.baidu.com/s',
params: {wd: 'jsliang'},
callBackkey: 'cb',
callback(res) {console.log(res);
}
})
4.2 简略实现
返回目录
咱们看看简略实现:
const JSONP = ({
url,
params = {},
callBackkey = 'cb',
callback,
}) => {params[callBackkey] = callBackkey;
window[callBackkey] = callback;
const newParam = Object.keys(params).map((key) => `${key}=${params[key]}`).join('&');
const script = document.createElement('script');
script.setAttribute('src', `${url}?${newParam}`);
document.body.appendChild(script);
}
JSONP({
url: 'https://www.baidu.com/s',
params: {wd: 'jsliang'},
callBackkey: 'cb',
callback(res) {console.log(res);
}
})
4.3 欠缺版本
返回目录
优化屡次调用时候的一个问题:
function JSONP({
url,
params = {},
callbackKey = "cb",
callback,
}) {
// 定义本地的惟一 callBackId,避免屡次调用的时候出问题
JSONP.callBackId = JSONP.callBackId || 1; // 默认为 1
// 拿到这个 id
const callBackId = JSONP.callBackId;
// 将要执行的回调如果到 JSONP 对象中,防止净化 window
JSONP.callbacks = JSONP.callbacks || [];
JSONP.callbacks[callBackId] = callback;
// 把这个名称退出到参数中:`cb = JSONP.callbacks[1]`
params[callbackKey] = `JSONP.callbacks[${callBackId}]`;
// 组合 params:'id=1&cb=JSONP.callbacks[1]'
const paramString = Object.keys(params).map((key) => `${key}=${params[key]}`);
// 动态创建 script 标签
const script = document.createElement("script");
script.setAttribute("src", `${url}?${paramString}`);
document.body.appendChild(script);
// id 自增,保障惟一
JSONP.callBackId++;
}
JSONP({
url: "http://localhost:8080/api/jsonp",
params: {id: 1},
callbackKey: "cb",
callback(res) {console.log(res);
},
});
JSONP({
url: "http://localhost:8080/api/jsonp",
params: {id: 2},
callbackKey: "cb",
callback(res) {console.log(res);
},
});
以上,就是手写局部的内容。
五 参考文献
返回目录
对于本文影响较大的是呆呆大佬的文章:
- [x] JSONP 原理及实现【浏览倡议:30min】
外面还有最终降级加强版,jsliang 就不颤抖了,面试问手写 JSONP
还没有碰到。
jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。