jsonp的核心原理是利用script标签没有同源限制的方式,可以发送跨域的get请求(只能发送get请求)。script标签中的src属性将请求参数和当前请求的回调函数名拼接在链接上。最终由服务端接收到请求之后拼接成前端可执行的字符串的形式返回。这个结果字符串最终会在前端的script
标签中解析并执行。
// 比如后端可以返回一下文本
jsonpCallbackName({
name: "lznism",
age: 10
});
发送jsonp请求分为以下几个步骤
- 将jsonp回调函数的名称callbackName拼接到src上
- 构建一个script标签,设置它的src属性
- 在全局设置一个callbackName回调函数,等待script标签请求结束,并调用
class JSONP {
/**
* 格式化数据
* {name: 'aa', age: 10} => name=aa&age=10
*/
formatData(data = {}) {
const arr = [];
for (let key in data) {
arr.push(encodeURIComponent(key) + "=" + data[key]);
}
return arr.join("&");
}
/**
* 初始化请求参数
* callbackName jsonp回调函数名称 必须
* url jsonp请求地址 必须
*/
initOptions() {
this.data = this.options.data || {};
this.callbackName = this.options.callbackName;
if (!this.callbackName) {
throw new Error("jsonp callbackName is required.");
}
this.url = this.options.url;
if (!this.url) {
throw new Error("jsonp url is required.");
}
this.head = document.querySelector("head");
this.script = document.createElement("script");
this.data["callback"] = this.callbackName;
this.data["timestamp"] = Date.now();
this.data = this.formatData(this.data);
}
request(options = {}) {
this.options = options;
this.initOptions();
this.script.src = this.url + "?" + this.data;
window[this.callbackName] = (jsonpData) => {
this.head.removeChild(this.script);
clearTimeout(this.script.timer);
window[this.callbackName] = null;
typeof this.options.success && this.options.success(jsonpData);
};
this.head.appendChild(this.script);
}
}