模拟axios利用promise封装一个自己的Ajax库。

一、基础框架

封装Ajax库之前,我们要将其框架结构写出来。

我们通过一个匿名函数,将我们的核心函数暴露给全局。

对这个核心函数进行方法(get、post等)的添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(function anonymous(window) {
//默认配置项
let _default = {
// 请求方式
method: "GET",
// URL
url: "",
// URL基
baseURL: "",
// 请求头
headers: {},
// 设置返回格式
dataType: "JSON",
// POST请求的数据
data: null,
// GET请求的数据
params: null,
// 缓存
cache: true,
};
// 定义函数
let ajaxPromise = function ajaxPromise() {};
// GET方法
ajaxPromise.get = function (url, options) {};
// POST方法
ajaxPromise.post = function (url, data, options) {};
// 暴露默认配置
ajaxPromise.defaults = _default;
// 将函数暴露给window
window.ajaxPromise = ajaxPromise;
})(window);

二、 管理Ajax请求

这一步来定义发送Ajax请求。即创建XMLHttpRequest核心对象,并发送请求。

请求发出后,根据定义的dataType进行对返回数据的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
let ajaxPromise = function ajaxPromise(options) {
// options中融合了 默认配置信息,用于基于defaults修改的信息、用户执行GET/POST方法时传递的配置信息。越靠后,优先级越高
let {
url,
baseURL,
method,
data,
params,
dataType,
headers,
cache,
} = options;
// 基于promise设计模式管理Ajax请求
return new Promise((resolve, reject) => {
// 创建核心对象
let xhr = new XMLHttpRequest();
// 发送请求
xhr.open(method, baseURL + url);
// 监听请求
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
// 如果成功则执行
if (/^(2|3)\d{2}$/.test(xhr.status)) {
// 更加dataType处理返回结果
let result = xhr.responseText;
dataType = dataType.toUpperCase();
// 根据设置类型进行对数据的处理
switch (dataType) {
case "JSON":
result = JSON.parse(result);
break;
case "XML":
result = xhr.responseXML;
break;
}
resolve(result, xhr);
return;
}
// 失败执行
reject(xhr.statusText, xhr);
}
};
// 发送请求数据
xhr.send(data);
});
};

在发送请求时,设置请求头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 发送请求
xhr.open(method, baseURL + url);
// 如果headers存在,设置请求头
if (headers !== null && typeof headers === "object") {
for (let attr in headers) {
if (headers.hasOwnProperty(attr)) {
// 处理中文
let value = headers[attr];
if (/[\u4e00-\u9fa5]/.test(value)) {
// 包含中文,将其编码为非中文
value = encodeURIComponent(value);
}
xhr.setRequestHeader(attr, value);
}
}
}

三、 根据请求方式处理传入参数

主要根据请求方式进行传递参数的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 处理传递的参数
if (/^(GET|DELETE|HEAD|OPTIONS)$/i.test(method)) {
// GET系列的
if (params) {
url += `${ajaxPromise.check(url)}${ajaxPromise.formateDate(params)}`;
}
// 判断是否缓存
if (cache === false) {
url += `${ajaxPromise.check(url)}_=${new Date()}`;
}
// GET系列请求主体就是什么都不放
data = null;
} else {
// POST系列
if (data) {
data = ajaxPromise.formateDate(data);
}
}
return new Promise((resolve, reject) => {
// code...
}

四、 封装方法

每一种请求方式实际上对应一个函数,因此我们可以将方法的定义修改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// GET系列
["get", "delete", "head", "options"].forEach((item) => {
ajaxPromise[item] = function anonymous(url, options) {
options = {
// 默认值
..._default,
// 用户调取方法传递的配置
...options,
// 请求的URL地址(第一个参数)
url: url,
//
method: item.toUpperCase(),
};
return ajaxPromise(options);
};
});
// POST系列
["post", "put", "patch"].forEach((item) => {
ajaxPromise[item] = function anonymous(url, data = {}, options = {}) {
options = {
// 默认值
..._default,
// 用户调取方法传递的配置
...options,
// 请求的URL地址(第一个参数)
url: url,
//
method: item.toUpperCase(),
data: data,
};
return ajaxPromise(options);
};
});

image-20200907184328385

五、 正常使用

封装好我们的Ajax库之后,可以使用一言接口简单的测试一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="ajaxPromise.js"></script>
</head>

<body>
<script>
ajaxPromise.get('https://v1.hitokoto.cn/').then(result => {
console.log(result.hitokoto);
document.write(`<p>${result.hitokoto}</p>`)
})
</script>
</body>

</html>

image-20200907184727633