# 实现 jsonp
jsonp 源码
# 基本原理
利用 script 标签的 src 没有跨域限制来完成跨域目的。
# 执行过程
- 前端定义一个解析函数:
jsonpCallBack = function (res) {}
- 通过 params 的形式包装 script 标签的请求参数,并且声明执行函数,如:
cb = jsonpCallBack
- 后端获取前端声明的执行函数(jsonpCallBack),并且带上参数且调用函数的方式传递给前端
- 前端再 script 标签返回资源的时候就会执行 jsonpCallBack 并通过回调函数的方式拿到数据。
# 优缺点
缺点:
- 只能进行 GET 请求
优点:
- 兼容性好,在一些古老的浏览器中都可以运行。
# 代码实现
# 👩💻:technologist: 简单版
function jsonp({
url,
params = {},
callbackKey = "jsonCallback", // 后端接收 callbackKey 参数获取 callbackName
callbackName = "cb" // 调用 window[callbackName](...args)
callback = () => {},
}) {
params[callbackKey] = callbackName
window[callbackName] = callback // 添加到 window 上
const paramsStr = Object.keys(params).reduce((res, key, i, data) => {
res += `${key}=${encodeURIComponent(params[key])}${i < data.length - 1 ? '&' : ''}`
return res
}, '')
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramsStr}`)
document.body.appendChild(script)
}
存在的问题,为了防止回调被覆盖,每个 jsonp 请求都需要传入 callbackName 并在 window 上定义 callbackName 函数,这样会对全局环境造成污染。
# 👩💻:technologist: 完整版 — 多个请求
- 让 callbackName 是一个唯一的,可以使用 id 递增的方式
- 把回调定义在 jsonp.cbs 数组上,避免污染全局环境
function jsonp ({
url,
params = {},
callbackKey = 'jsonCallback',
callback = () => {},
}) {
const cid = jsonp.cid = (jsonp.cid || 0)
jsonp.cbs = jsonp.cbs || []
jsonp.cbs[cid] = callback
params[callbackKey] = `jsonp.cbs[${cid}]`
// 拼接参数
const paramsStr = Object.keys(params).reduce((res, key, i, data) => {
res += `${key}=${encodeURIComponent(params[key])}${i < data.length - 1 ? '&' : ''}`
return res
}, '')
// 创建 script
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramsStr}`)
document.body.appendChild(script)
jsonp.cid++ // id 自增
}