fetchJsonp源码
源码地址:https://github.com/camsong/fetch-jsonp
jsonp优势
-
请求数据没有跨域的限制,后台不用考虑跨域问题
-
对于老版本浏览器更加支持
jsonp缺陷
- 只支持get请求,不支持其他所有方式的请求(请求方式受到了限制)
- 只支持get请求,不支持post(不安全因素一)
- 因为没有跨域,所以调用接口方不能限制ip,安全方面不是很到位(不安全因素二)
使用例子
import fetchJsonp from 'fetch-jsonp';
fetchJsonp(`${document.location.protocol}//xxx.com/v1/pages/list`).then(
(res) => {
return res.json();
},
);
fetchJsonp函数
主要是利用script标签的src属性来请求api,实现跨域。
function fetchJsonp(_url, options = {}) {
// to avoid param reassign
let url = _url;
const timeout = options.timeout || defaultOptions.timeout;
// defaultOptions.jsonpCallback,默认是'callback'
const jsonpCallback = options.jsonpCallback || defaultOptions.jsonpCallback;
let timeoutId;
return new Promise((resolve, reject) => {
// 回调函数名称,字符串类型
const callbackFunction =
// generateCallbackFunction返回一个随机定义的字符串
options.jsonpCallbackFunction || generateCallbackFunction();
//用于script标签的id
const scriptId = `${jsonpCallback}_${callbackFunction}`;
// 将属性callbackFunction定义在window全局对象上,简单概括就是在window全局对象上定义一个函数
window[callbackFunction] = (response) => {
// 返回信息
resolve({
ok: true,
// keep consistent with fetch API
json: () => Promise.resolve(response),
});
// 清除定时器
if (timeoutId) clearTimeout(timeoutId);
removeScript(scriptId);
clearFunction(callbackFunction);
};
// Check if the user set their own params, and if not add a ? to start a list of params
url += url.indexOf('?') === -1 ? '?' : '&';
// 创建一个script标签
const jsonpScript = document.createElement('script');
// 设置src属性
jsonpScript.setAttribute(
'src',
`${url}${jsonpCallback}=${callbackFunction}`,
);
// 编码
if (options.charset) {
jsonpScript.setAttribute('charset', options.charset);
}
// nonce 全局属性是定义了密码学 nonce(“只使用一次的数字”)的内容属性,内容安全策略可以使用它来确定是否允许对给定元素进行获取。
if (options.nonce) {
jsonpScript.setAttribute('nonce', options.nonce);
}
//
if (options.referrerPolicy) {
jsonpScript.setAttribute('referrerPolicy', options.referrerPolicy);
}
// 设置跨域
if (options.crossorigin) {
jsonpScript.setAttribute('crossorigin', 'true');
}
jsonpScript.id = scriptId;
// 返回一个包括所有给定标签名称的元素的 HTML 集合HTMLCollection
// 在head标签之后添加script标签
document.getElementsByTagName('head')[0].appendChild(jsonpScript);
// 定时器
timeoutId = setTimeout(() => {
reject(new Error(`JSONP request to ${_url} timed out`));
// callbackFunction,移除window上的自定义的callbackFunction
clearFunction(callbackFunction);
// 移除script标签
removeScript(scriptId);
//
window[callbackFunction] = () => {
clearFunction(callbackFunction);
};
}, timeout);
// Caught if got 404/500
jsonpScript.onerror = () => {
// 抛出错误
reject(new Error(`JSONP request to ${_url} failed`));
clearFunction(callbackFunction);
removeScript(scriptId);
if (timeoutId) clearTimeout(timeoutId);
};
});
}
export default fetchJsonp;
defaultOptions变量
const defaultOptions = {
timeout: 5000,
jsonpCallback: 'callback',
jsonpCallbackFunction: null,
};
generateCallbackFunction函数
返回一个自定义的随机字符串
function generateCallbackFunction() {
return `jsonp_${Date.now()}_${Math.ceil(Math.random() * 100000)}`;
}
removeScript函数
移除script标签
function removeScript(scriptId) {
// 找到script标签
const script = document.getElementById(scriptId);
// 移除script标签
if (script) {
document.getElementsByTagName('head')[0].removeChild(script);
}
}
clearFunction
移除全局对象window上的自定义方法或属性
function clearFunction(functionName) {
// 移除window上自定义属性or方法
try {
delete window[functionName];
} catch (e) {
window[functionName] = undefined;
}
}
打包
利用Babel库打包
{
"scripts": {
"prepublish": "npm run lint && npm run clean && npm run build",
"build": "babel src/ --modules umd --out-dir build",
"clean": "rm -rf build",
"lint": "eslint src/"
}
}
babel
Babel是一个帮助你用最新版本的JavaScript编写代码的工具。当您的支持环境不支持某些特性时,Babel将帮助您将这些特性编译为支持的版本。
// ES2020 nullish coalescing
function greet(input) {
return input ?? "Hello world";
}
function greet(input) {
return input != null ? input : "Hello world";
}