现在流行的 Ajax 库,都是基于 Promise 实现的,如:axios。所以基于 Promise 封装一个简单的 Ajax 库,来理解其原理。
参数设置
options: [Object]
type: [String] 请求方式,默认 get
url: [String] 请求地址
data: [Object] 发送的数据
dataType: [String] 数据类型,json/xml/text,默认 json
cache: [Boolean] 是否缓存,默认不缓存
默认都是异步操作,不支持同步
整体结构
使用一个闭包封装,然后通过挂载到 window 来使用,借用了 JQuery 的思想
;(function(window) {
function ajaxPromise(options) {
//...
}
window.ajax = ajaxPromise;
})(window)
构造函数
由于这个方法基于 Promise 来管理 Ajax 操作,所以,构造函数必定返回一个 Promise 实例。
在这个 Promise 实例中的回调函数中执行 Ajax 操作,把参数的处理,放到外面。
function ajaxPromise(opts) {
//=> 处理各种参数
return new Promise(function (resolve, reject) {
//=> 这里执行原生 Ajax
let xhr = new XMLHttpRequest();
xhr.open(type, url); // 默认异步
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /^2\d{2}|304/.test(xhr.status)) {
//=> 成功
// 处理数据类型
//...
resolve(data);
}
if (xhr.readyState === 4 && /^[45]\d{2}/.test(xhr.status)) {
//=> 失败
reject(xhr);
}
};
//=> 设置请求头,使得后台接受的参数是 form data,而不是字符串
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=utf-8');
xhr.send(data);
})
}
参数的处理与前面封装的 Ajax 库一样,这里不再赘述。
这里只需要简单的在 Ajax 请求成功后,执行 resolve,失败后执行 reject 即可。
代码
; (function (window) {
function ajaxPromise(opts) {
let {
url,
type = 'get',
data = {},
dataType = 'json',
cache = false
} = opts;
//=> 处理 data
if (typeof data === 'object') {
let str = '';
for (let k in data) {
if (data.hasOwnProperty(k)) {
str += `${k}=${data[k]}&`;
}
}
data = str.slice(0, str.length - 1);
}
// 判断是否是 get 类型请求
if (/^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i.test(type)) {
let char = '';
if (url.indexOf('?') === -1) {
char = `?`;
} else {
url = url.replace(/&$/, '');
char = `&`;
}
url += `${char}${data}`;
data = null;
}
//=> 处理 cache
if (/^GET$/i.test(type) && cache === false) {
//=> url 末尾追加时间戳
let char = '';
if (url.indexOf('?') === -1) {
char = `?`;
} else {
url = url.replace(/&$/, '');
char = `&`;
}
url += `${char}_=${+(new Date())}`;
this.url = url;
}
return new Promise(function (resolve, reject) {
//=> 这里执行原生 Ajax
let xhr = new XMLHttpRequest();
xhr.open(type, url); // 默认异步
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /^2\d{2}|304/.test(xhr.status)) {
//=> 成功
// 处理数据类型
let data = xhr.responseText;
switch (dataType.toLowerCase()) {
case 'json':
data = JSON.parse(data);
break;
case 'xml':
data = xhr.responseXML;
case 'text':
break;
default:
}
resolve(data);
}
if (xhr.readyState === 4 && /^[45]\d{2}/.test(xhr.status)) {
//=> 失败
reject(xhr);
}
};
//=> 设置请求头,使得后台接受的参数是 form data,而不是字符串
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=utf-8');
xhr.send(data);
})
}
window.ajaxPromise = ajaxPromise;
}) (window);