Axios学习笔记

视频来源:黑马

笔记参考:努力学习的汪

学习时间:2022年2月28日,2024年7月31日

1 预备工具

1.1 安装

json-server可用于模拟后端。

  • 安装
1
npm install -g json-server
  • axios文件夹下创建db.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"post": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "Mark
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
  • 在当前文件夹下启动json-server服务
1
json-server --watch db.json

注意事项

在执行上面的命令时,可能会报以下错误:

1
无法加载文件 json-server.ps1,因为在此系统上禁止运行脚本。

解决办法:

  • 以管理员身份运行vscode
  • 在终端执行:
1
2
get-ExecutionPolicy # 显示Restricted,表示状态是禁止的
set-ExecutionPolicy RemoteSigned

1.2 使用

服务启动后,我们可以以Rest的方式请求这些地址:

image-20220228195916450

例如获取id为2的post数据,可访问接口:http://localhost/post/2

image-20220228200029859

2 axios的理解与使用

2.1 概述

2.1.1 axios是什么

  1. 前端最流行的 ajax 请求库
  2. react/vue 官方都推荐使用 axios 发 ajax 请求
  3. 文档: https://github.com/axios/axios

2.1.2 axios特点

  1. 基于 xhrXMLHttpRequest) + promise异步 ajax 请求库
  2. 浏览器端/node 端都可以使用
  3. 支持请求/响应拦截器
  4. 支持请求取消
  5. 请求/响应数据转换
  6. 批量发送多个请求

2.1.3 axios常用语法

  1. axios(config): 通用/最本质的发任意类型请求的方式
  2. axios(url[, config]): 可以只指定 url 发 get 请求
  3. axios.request(config): 等同于 axios(config)
  4. axios.get(url[, config]): 发 get 请求
  5. axios.delete(url[, config]): 发 delete 请求
  6. axios.post(url[, data, config]): 发 post 请求
  7. axios.put(url[, data, config]): 发 put 请求
  8. axios.defaults.xxx: 请求的默认全局配置
  9. axios.interceptors.request.use(): 添加请求拦截器
  10. axios.interceptors.response.use(): 添加响应拦截器
  11. axios.create([config]): 创建一个新的 axios(它没有下面的功能)
  12. axios.Cancel(): 用于创建取消请求的错误对象
  13. axios.CancelToken(): 用于创建取消请求的 token 对象
  14. axios.isCancel(): 是否是一个取消请求的错误
  15. axios.all(promises): 用于批量执行多个异步请求
  16. axios.spread(): 用来指定接收所有成功数据的回调函数的方法

2.1.4 原理图

image-20220228200820716

2.2 基本使用

2.2.1 安装

有多种方式安装axios:

  • 使用npm或yarn全局安装:项目中使用
1
npm install axios
1
yarn add axios
  • 通过在页面中CDN来引入:学习使用
1
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
1
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
</head>
<body>
<script>
console.log(axios);
</script>
</body>
</html>

image-20220228201451099

2.2.2 发送不同类型的请求

对应常用语法的第1条。

  • 基础页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!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="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h2 class="page-header">基本使用</h2>
<button class="btn btn-primary">发送GET请求</button>
<button class="btn btn-warning">发送POST请求</button>
<button class="btn btn-success">发送PUT请求</button>
<button class="btn btn-danger">发送DELETE请求</button>
</div>
<script>
// 获取按钮
const btns = document.querySelectorAll("button");
</script>
</body>
</html>

image-20220228202915447

  • 发送GET请求

    • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 发送GET请求
    btns[0].onclick = function() {
    axios({
    // 请求类型
    method: "GET",
    // 请求URL
    url: "http://localhost:3000/posts/2"
    }).then(response => {
    // 打印响应信息
    console.log(response);
    });
    };
    • 响应信息

    image-20220228203206836

  • 发送POST请求:例如新增一个文档

    • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 发送POST请求
    // 添加一篇新的文章
    btns[1].onclick = function() {
    axios({
    // 请求类型
    method: "POST",
    // 请求URL
    url: "http://localhost:3000/posts",
    // 设置请求体
    data: {
    title: "Today",
    author: "Hongyi"
    }
    }).then(response => {
    console.log(response);
    });
    };
    • 执行结果:

    image-20220228203315038

  • 发送PUT请求:例如更新一篇文档

    • 代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 发送PUT请求
    btns[2].onclick = function() {
    axios({
    // 请求类型
    method: "PUT",
    // 请求URL
    url: "http://localhost:3000/posts/3",
    // 设置请求体
    data: {
    title: "Today",
    author: "Zhangsan"
    }
    }).then(response => {
    console.log(response);
    });
    };
    • 执行结果:

    image-20220228203427246

    image-20220228203451479

  • 发送DELETE请求

    • 代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 发送DELETE请求
    btns[3].onclick = function() {
    axios({
    // 请求类型
    method: "DELETE",
    // 请求URL
    url: "http://localhost:3000/posts/3"
    }).then(response => {
    console.log(response);
    });
    };

总结

axios传入一个对象,对象的属性必填的有:

  • method:请求方法
  • url:请求的接口地址

2.2.3 其他方式发送请求

对应常用语法的3-7条。

  • axios.request(config),等同于axios(config)
1
2
3
4
5
6
7
8
9
// 发送GET请求
btns[0].onclick = function() {
axios.request({
method: "GET",
url: "http://localhost:3000/comments"
}).then(response => {
console.log(response);
});
}
  • 发送post请求
1
2
3
4
5
6
7
8
9
10
11
12
btns[1].onclick = function() {
axios.post(
// url
"http://localhost:3000/comments",
// 请求体
{
"body": "Good morning",
"postId": 2
}).then(response => {
console.log(response);
});
};

其余请求写法类似。

2.2.4 请求响应结果的结构

即回调函数中response的结构。以GET请求为例:

image-20220228205918925

2.2.5 请求的配置对象

即发送axios请求时:axios(config)中的config对象

These are the available config options for making requests. Only the url is required. Requests will default to GET if method is not specified.

以下字段只摘取重要的字段进行注释:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
url: '/user',

method: 'get', // default

// 设定url的基础结构
baseURL: 'https://some-domain.com/api/',

// 对请求的数据进行处理
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data

return data;
}],

transformResponse: [function (data) {
// Do whatever you want to transform the data

return data;
}],

// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},

// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
// 例如 url?a=100&ID=12345
params: {
a: 100,
ID: 12345
},

// 对请求参数做序列化
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},

// 请求体数据,作为请求体数据
// 对象的写法
data: {
firstName: 'Fred'
},

// 请求数据,作为url的参数
// 字符串的写法
data: 'Country=Brasil&City=Belo Horizonte',

// 超时设置,ms
timeout: 1000, // default is `0` (no timeout)

// `withCredentials` indicates whether or not cross-site Access-Control requests
// should be made using credentials
withCredentials: false, // default

adapter: function (config) {
/* ... */
},

auth: {
username: 'janedoe',
password: 's00pers3cret'
},

// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default

// `responseEncoding` indicates encoding to use for decoding responses (Node.js only)
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default

// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default

// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default

// `maxContentLength` defines the max size of the http response content in bytes allowed in node.js
maxContentLength: 2000,

// `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed
maxBodyLength: 2000
// ...以下不再列出
}

2.2.6 默认配置

对应常用语法的第8条:axios.defaults.xxx: 请求的默认全局配置

例如配置默认请求方式、url携带参数等。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取按钮
const btns = document.querySelectorAll("button");
// 默认配置
axios.defaults.method = "GET";
axios.defaults.baseURL = "http://localhost:3000";
axios.defaults.params = {
id: 100
};
axios.defaults.timeout = 3000;

// 默认发送GET请求
btns[0].onclick = function() {
axios({
url: "/posts"
}).then(response => {
console.log(response);
});
};

image-20220301142442183

2.2.7 创建实例对象发送请求

对应常用语法第11条:axios.create([config]): 创建一个新的 axios

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建axios实例对象
const joke = axios.create({
baseURL: "https://api.apiopen.top",
timeout: 2000
});
// 这里的joke与axios对象的功能 几乎一样
console.log(joke);

// 方式1
joke({
method: "GET",
url: "/getJoke"
}).then(response => {
console.log(response);
})

// 方式2
joke.get("/getJoke").then(response => {
console.log(response.data);
});

2.2.8 拦截器

  • 拦截器(Interceptor)的分类
    • 请求拦截器
    • 响应拦截器
  • 拦截器的作用
    • 在请求发送前和接收响应前,对请求数据和响应数据做一些处理

代码示例

  • 情形①:请求拦截器成功运行
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
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功");
return config;
}, function (error) {
console.log("请求拦截器 失败")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功");
return response;
}, function (error) {
console.log("响应拦截器 失败");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
请求拦截器 成功
响应拦截器 成功
自定义回调处理成功的结果
  • 情形②:请求拦截器抛出错误
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
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功");
// return config;
// 抛出一个异常
throw "参数异常";
}, function (error) {
console.log("请求拦截器 失败")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功");
return response;
}, function (error) {
console.log("响应拦截器 失败");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
请求拦截器 成功
响应拦截器 失败
自定义失败回调
  • 情形③:设置分别两个请求和响应拦截器,注意拦截顺序
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
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功 - 1号");
return config;
}, function (error) {
console.log("请求拦截器 失败 - 1号")
return Promise.reject(error);
});

// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功 - 2号");
return config;
}, function (error) {
console.log("请求拦截器 失败 - 2号")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功 - 1号");
return response;
}, function (error) {
console.log("响应拦截器 失败 - 1号");
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功 - 2号");
return response;
}, function (error) {
console.log("响应拦截器 失败 - 2号");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
4
5
请求拦截器 成功 - 2号
请求拦截器 成功 - 1号
响应拦截器 成功 - 1号
响应拦截器 成功 - 2号
自定义回调处理成功的结果

请求拦截器的拦截顺序是倒序的,而响应拦截器的拦截顺序是顺序的。

拦截器中的形参

  • 请求拦截器的参数

    • config:即axios的config属性,在返回该对象时可以自定义进行处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 设置请求拦截器
    axios.interceptors.request.use(function (config) {
    console.log("请求拦截器 成功 - 1号");
    // 修改config的参数
    config.params = {
    a: 100
    };
    return config;
    }, function (error) {
    console.log("请求拦截器 失败 - 1号")
    return Promise.reject(error);
    });

    // 设置请求拦截器
    axios.interceptors.request.use(function (config) {
    console.log("请求拦截器 成功 - 2号");
    // 修改config的参数
    config.timeout = 2000;
    return config;
    }, function (error) {
    console.log("请求拦截器 失败 - 2号")
    return Promise.reject(error);
    });

    image-20220301150030718

  • 响应拦截器的参数

    • response:即then回调函数的形参,为响应信息,同样也可以进行自定义处理,例如只返回响应消息的响应体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 设置响应拦截器
    axios.interceptors.response.use(function (response) {
    console.log("响应拦截器 成功 - 1号");
    // return response;
    // 只返回响应体的结果
    return response.data;
    }, function (error) {
    console.log("响应拦截器 失败 - 1号");
    return Promise.reject(error);
    });

2.2.9 取消请求

为防止axios发送请求后得到响应过快,可在json服务器端设置延迟响应,这样可观察到取消请求的功能。

1
json-server --watch db.json -d 2000 # 延迟2s响应

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 获取按钮
const btns = document.querySelectorAll("button");
// 2.声明一个全局变量
let cancel = null;

btns[0].onclick = function() {
axios({
method: "GET",
url: "http://localhost:3000/posts",
// 1.添加配置对象的属性
cancelToken: new axios.CancelToken(function(c) {
// 3.将c赋值给cancel
cancel = c;
})
}).then(response => {
console.log(response.data);
})
};

// 取消请求
btns[1].onclick = function() {
cancel();
};

image-20220301151248249

完善

用户多次点击发送请求,势必会给服务器带来处理压力,因此当用户多次点击发送请求时,检查上次请求是否已经得到响应,如果没有,则取消上次请求。

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
// 获取按钮
const btns = document.querySelectorAll("button");
// 2.声明一个全局变量
let cancel = null;

btns[0].onclick = function() {
// 检测上一次请求是否已经完成
if(cancel !== null) {
// 取消上一次的请求
cancel();
}

axios({
method: "GET",
url: "http://localhost:3000/posts",
// 1.添加配置对象的属性
cancelToken: new axios.CancelToken(function(c) {
// 3.将c赋值给cancel
cancel = c;
})
}).then(response => {
console.log(response.data);
// 请求响应时,cancel置空
cancel = null;
})
};

// 取消请求
btns[1].onclick = function() {
cancel();
};

image-20220301151900184

3 axios请求封装

3.1 JavaScript版

3.1.1 代码结构

1
2
3
4
5
6
7
8
9
10
11
project
│ package.json
│ ...
└───src
├───api
│ ├───http.js
│ ├───user
│ ├───user.js
│ ├───其他业务模块的api请求
└───components
└───UserComponent.vue

3.1.2 步骤

src/api/http.js

  • 创建axios实例,设置默认配置

  • 配置请求拦截

  1. localStorage 中获取 token 和 refresh token。
  2. 如果 token 存在,将其添加到请求头中。
  3. 检查 token 是否即将过期,如果即将过期则使用 refresh token 刷新 token。
  4. 将新的 token 存储在 localStorage 中,并更新请求头。
  5. 如果刷新 token 失败,清除 localStorage 中的 token 和 refresh token,并重定向到登录页面。
  • 配置响应拦截:通常用于处理服务器返回的响应数据和错误。
  1. 处理响应数据:解析并返回实际需要的数据部分。
  2. 处理错误响应:根据不同的 HTTP 状态码进行相应的错误处理。
  3. 全局错误提示:统一处理错误提示,例如显示错误消息。
  4. 自动刷新 Token:在响应中检测到特定错误码(如 401 未授权)时,尝试自动刷新 Token 并重新发起请求。
  • 导出模块

src/api/user/user.js

  • 封装业务请求

3.1.3 代码实现

  • 文件:src/api/http.js(默认配置,请求/响应拦截器配置,导出模块)
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import axios from 'axios';

// 创建 axios 实例
const instance = axios.create({
baseURL: 'https://api.example.com', // 替换为你的 API 基础 URL
timeout: 10000, // 请求超时时间
headers: {
'Content-Type': 'application/json',
},
});

// 添加请求拦截器
instance.interceptors.request.use(
async config => {
let token = localStorage.getItem('token'); // 从 localStorage 中获取 token
const refreshToken = localStorage.getItem('refreshToken'); // 获取 refresh token

if (token) {
config.headers.Authorization = `Bearer ${token}`;
}

// 检查 token 是否即将过期,刷新 token 的逻辑
if (token && refreshToken && isTokenExpiringSoon(token)) {
try {
const response = await axios.post('https://api.example.com/auth/refresh', { token: refreshToken });
token = response.data.token;
localStorage.setItem('token', token);
config.headers.Authorization = `Bearer ${token}`;
} catch (error) {
console.error('刷新 token 失败', error);
// 处理刷新 token 失败的情况,比如跳转到登录页面
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
}
}
return config;
},
error => {
return Promise.reject(error);
}
);

// 检查 token 是否即将过期的辅助函数
function isTokenExpiringSoon = (token) => {
const payload = JSON.parse(atob(token.split('.')[1]));
const exp = payload.exp * 1000; // token 的过期时间,单位为毫秒
const now = Date.now();
return exp - now < 60000; // 如果 token 在 1 分钟内过期,则认为它即将过期
}

// 添加响应拦截器
instance.interceptors.response.use(
response => {
// 处理响应数据
// 可以在这里对返回的数据进行处理,比如只返回 data 部分
return response.data;
},
async error => {
// 处理响应错误
if (error.response) {
switch (error.response.status) {
case 401: // 未授权
console.error('未授权,请登录');
// 尝试刷新 Token
const refreshToken = localStorage.getItem('refreshToken');
if (refreshToken) {
try {
const response = await axios.post('https://api.example.com/auth/refresh', { token: refreshToken });
const newToken = response.data.token;
localStorage.setItem('token', newToken);
// 重新发起原始请求
error.config.headers.Authorization = `Bearer ${newToken}`;
return axios(error.config);
} catch (refreshError) {
console.error('刷新 token 失败', refreshError);
// 清除 token 并重定向到登录页面
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
}
} else {
// 没有 refresh token 的情况,直接跳转到登录页面
window.location.href = '/login';
}
break;
case 403: // 禁止访问
console.error('禁止访问');
// 可以在这里添加禁止访问的处理逻辑
break;
case 404: // 资源未找到
console.error('资源未找到');
// 可以在这里添加资源未找到的处理逻辑
break;
default:
console.error('其他错误', error.response.status);
// 处理其他错误
}
} else {
// 处理没有响应的情况,比如网络错误
console.error('网络错误或请求未发送成功');
}

// 全局错误提示
// 可以使用一些 UI 库来展示错误提示,比如 toast
// toast.error('请求出错,请稍后重试');

return Promise.reject(error);
}
);

export default instance;
  • 文件:src/api/user/user.js,与用户相关的API封装
1
2
3
4
5
6
7
8
9
10
11
12
13
import http from './http';

// 用户登录
export const login = (credentials) => http.post('/auth/login', credentials);

// 获取用户信息
export const getUserInfo = () => http.get('/user/profile');

// 更新用户信息
export const updateUserInfo = (data) => http.put('/user/profile', data);

// 获取用户列表
export const getUserList = () => http.get('/user/list');

3.1.4 使用

在组件中使用封装好的业务 API:

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
<template>
<div>
<h1>User Information</h1>
<div v-if="user">
<p>Name: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
</div>
<button @click="fetchUserInfo">Fetch User Info</button>
<button @click="loginUser">Login</button>
</div>
</template>

<script setup>
import { ref } from 'vue';
import { getUserInfo, login } from '../api/user';

// 定义响应式数据
const user = ref(null);
const credentials = ref({
email: 'user@example.com',
password: 'password123',
});

// 定义方法
const fetchUserInfo = async () => {
try {
user.value = await getUserInfo();
} catch (error) {
console.error('获取用户信息失败', error);
}
};

const loginUser = async () => {
try {
const response = await login(credentials.value);
console.log('登录成功', response);
} catch (error) {
console.error('登录失败', error);
}
};
</script>

3.2 TypeScript版

用 TypeScript 封装 Axios 需要安装以下几个库:

  1. Axios:用于 HTTP 请求。
  2. TypeScript:用于类型检查和编译。
  3. @types/axios:提供 Axios 的 TypeScript 类型定义。
1
2
3
npm install axios
npm install typescript --save-dev
npm install @types/axios --save-dev

3.2.1 代码结构

1
2
3
4
5
6
7
8
9
10
my-vue-project/
├── src/
│ ├── api/
│ │ ├── user.ts /* 业务请求封装 */
│ │ ├── list.ts /* 业务请求封装 */
│ ├── components/
│ │ ├── UserList.vue
│ ├── utils/
│ │ ├── request.ts /* 基本请求封装 */
│ │ ├── result.ts /* 响应结果统一封装 */

3.2.2 代码实现

  • src/utils/result.ts:统一响应结果
1
2
3
4
5
export interface ResultData<T> {
success: boolean;
message: string;
data: T;
}
  • src/utils/request.ts:使用类实现
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// src/utils/request.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { ResultData } from './result';

class HttpService {
private axiosInstance: AxiosInstance;

constructor(baseURL: string) {
this.axiosInstance = axios.create({
baseURL,
timeout: 10000, // 超时时间
headers: {
'Content-Type': 'application/json',
},
});

this.initializeRequestInterceptor();
this.initializeResponseInterceptor();
}

private initializeRequestInterceptor() {
this.axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 在发送请求之前做些什么,例如添加 token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
}

private initializeResponseInterceptor() {
this.axiosInstance.interceptors.response.use(
(response: AxiosResponse) => {
// 对响应数据做些什么
return response.data;
},
(error) => {
// 对响应错误做些什么
if (error.response) {
// 根据状态码处理不同的错误
switch (error.response.status) {
case 401:
// 未授权,可以跳转到登录页面
break;
case 403:
// 拒绝访问
break;
case 500:
// 服务器错误
break;
default:
// 其他错误
break;
}
}
return Promise.reject(error);
}
);
}

/**
@Param url: string 是方法的第一个参数,表示请求的 URL,类型为 string。
@Param config?: AxiosRequestConfig 是方法的可选参数,表示请求的配置选项,类型为AxiosRequestConfig。
@Return Promise<ResultData<T>> 表示该方法返回一个 Promise 对象,Promise 解析时的类型为 ResultData<T>。
*/
public get<T>(url: string, config?: AxiosRequestConfig): Promise<ResultData<T>> {
return this.axiosInstance.get<ResultData<T>>(url, config);
}

public post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<ResultData<T>> {
return this.axiosInstance.post<ResultData<T>>(url, data, config);
}

// 其他 HTTP 方法...
}

const httpService = new HttpService('https://api.example.com');
export default httpService;
  • 封装常用的业务请求

src/api/user.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/api/user.ts
import httpService from '@/utils/request';
import { ResultData } from '@/utils/result';

// 定义接口返回的数据类型
interface UserResponse {
id: number;
name: string;
email: string;
}

// 获取用户信息
/**
1. (): Promise<UserResponse>:表明函数没有参数,返回值为一个 Promise 对象,Promise 对象在解析时会返回一个 UserResponse 类型的值。
2. 函数体为箭头函数的形式。
*/
export const getUserInfo = (): Promise<ResultData<UserResponse>> => {
return httpService.get<ResultData<UserResponse>>('/user/info');
};

// 注:传统函数的形式
export function getUserInfo(): Promise<ResultData<UserResponse>> {
return httpService.get<ResultData<UserResponse>>('/user/info');
};

src/api/list.ts

1
2
3
4
5
6
7
8
9
10
11
12
// src/api/list.ts
import httpService from '@/utils/request';

// 定义接口返回的数据类型
interface ListResponse {
items: Array<{ id: number; name: string }>;
}

// 获取列表数据
export const getListData = (): Promise<ResultData<ListResponse>> => {
return httpService.get<ResultData<ListResponse>>('/list/data');
};

3.2.3 使用

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
<template>
<div>
<h1>用户信息</h1>
<div v-if="user">
<p>名称: {{ user.name }}</p>
<p>邮箱: {{ user.email }}</p>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { getUserInfo } from '@/api/user';
import { ResultData } from '@/utils/result'

interface User {
id: number;
name: string;
email: string;
}

const user = ref<User | null>(null);

onMounted(async () => {
try {
const userInfo: ResultData<User> = await getUserInfo();
if (userInfo.success) {
user.value = userInfo.data;
} else {
console.error('出错');
}
} catch (error) {
console.error(error);
}
});
</script>