Axios学习笔记 视频来源:黑马
笔记参考:努力学习的汪
学习时间:2022年2月28日,2024年7月31日
1 预备工具 1.1 安装 json-server
可用于模拟后端。
1 npm install -g json-server
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" } }
1 json-server --watch db.json
注意事项
在执行上面的命令时,可能会报以下错误:
1 无法加载文件 json-server.ps1,因为在此系统上禁止运行脚本。
解决办法:
1 2 get-ExecutionPolicy set-ExecutionPolicy RemoteSigned
1.2 使用 服务启动后,我们可以以Rest的方式请求这些地址:
例如获取id为2的post数据,可访问接口:http://localhost/post/2
2 axios的理解与使用 2.1 概述 2.1.1 axios是什么
前端最流行的 ajax 请求库
react/vue 官方都推荐使用 axios 发 ajax 请求
文档: https://github.com/axios/axios
2.1.2 axios特点
基于 xhr
(XMLHttpRequest
) + promise
的异步 ajax 请求库
浏览器端/node 端都可以使用
支持请求/响应拦截器
支持请求取消
请求/响应数据转换
批量发送多个请求
2.1.3 axios常用语法
axios(config): 通用/最本质
的发任意类型请求的方式
axios(url[, config]): 可以只指定 url 发 get 请求
axios.request(config): 等同于 axios(config)
axios.get(url[, config]): 发 get 请求
axios.delete(url[, config]): 发 delete 请求
axios.post(url[, data, config]): 发 post 请求
axios.put(url[, data, config]): 发 put 请求
axios.defaults.xxx: 请求的默认全局配置
axios.interceptors.request.use(): 添加请求拦截器
axios.interceptors.response.use(): 添加响应拦截器
axios.create([config]): 创建一个新的 axios(它没有下面的功能)
axios.Cancel(): 用于创建取消请求的错误对象
axios.CancelToken(): 用于创建取消请求的 token 对象
axios.isCancel(): 是否是一个取消请求的错误
axios.all(promises): 用于批量执行多个异步请求
axios.spread(): 用来指定接收所有成功数据的回调函数的方法
2.1.4 原理图
2.2 基本使用 2.2.1 安装 有多种方式安装axios:
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 >
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 >
发送GET请求
1 2 3 4 5 6 7 8 9 10 11 12 btns[0 ].onclick = function ( ) { axios ({ method : "GET" , url : "http://localhost:3000/posts/2" }).then (response => { console .log (response); }); };
发送POST请求:例如新增一个文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 btns[1 ].onclick = function ( ) { axios ({ method : "POST" , url : "http://localhost:3000/posts" , data : { title : "Today" , author : "Hongyi" } }).then (response => { console .log (response); }); };
发送PUT请求:例如更新一篇文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 btns[2 ].onclick = function ( ) { axios ({ method : "PUT" , url : "http://localhost:3000/posts/3" , data : { title : "Today" , author : "Zhangsan" } }).then (response => { console .log (response); }); };
发送DELETE请求
1 2 3 4 5 6 7 8 9 10 11 btns[3 ].onclick = function ( ) { axios ({ method : "DELETE" , url : "http://localhost:3000/posts/3" }).then (response => { console .log (response); }); };
总结
axios传入一个对象,对象的属性必填的有:
2.2.3 其他方式发送请求 对应常用语法的3-7条。
axios.request(config)
,等同于axios(config)
1 2 3 4 5 6 7 8 9 btns[0 ].onclick = function ( ) { axios.request ({ method : "GET" , url : "http://localhost:3000/comments" }).then (response => { console .log (response); }); }
1 2 3 4 5 6 7 8 9 10 11 12 btns[1 ].onclick = function ( ) { axios.post ( "http://localhost:3000/comments" , { "body" : "Good morning" , "postId" : 2 }).then (response => { console .log (response); }); };
其余请求写法类似。
2.2.4 请求响应结果的结构 即回调函数中response
的结构。以GET请求为例:
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', baseURL: 'https: transformRequest: [ function (data, headers) { return data; } ] , transformResponse: [ function (data) { return data; } ] , headers: { 'X-Requested-With': 'XMLHttpRequest'} , params: { a: 100 , ID: 12345 } , paramsSerializer: function (params) { return Qs.stringify(params, { arrayFormat: 'brackets'} ) } , data: { firstName: 'Fred' } , data: 'Country=Brasil&City=Belo Horizonte', timeout: 1000 , withCredentials: false , adapter: function (config) { } , auth: { username: 'janedoe', password: 's00pers3cret' } , responseType: 'json', responseEncoding: 'utf8', xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: 2000 , 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 ; btns[0 ].onclick = function ( ) { axios ({ url : "/posts" }).then (response => { console .log (response); }); };
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 const joke = axios.create ({ baseURL : "https://api.apiopen.top" , timeout : 2000 }); console .log (joke);joke ({ method : "GET" , url : "/getJoke" }).then (response => { console .log (response); }) 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 ("请求拦截器 成功" ); 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.params = { a : 100 }; return config; }, function (error ) { console .log ("请求拦截器 失败 - 1号" ) return Promise .reject (error); }); axios.interceptors .request .use (function (config ) { console .log ("请求拦截器 成功 - 2号" ); config.timeout = 2000 ; return config; }, function (error ) { console .log ("请求拦截器 失败 - 2号" ) return Promise .reject (error); });
响应拦截器的参数
response
:即then回调函数的形参,为响应信息,同样也可以进行自定义处理,例如只返回响应消息的响应体
1 2 3 4 5 6 7 8 9 10 axios.interceptors .response .use (function (response ) { console .log ("响应拦截器 成功 - 1号" ); 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
代码示例
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" );let cancel = null ;btns[0 ].onclick = function ( ) { axios ({ method : "GET" , url : "http://localhost:3000/posts" , cancelToken : new axios.CancelToken (function (c ) { cancel = c; }) }).then (response => { console .log (response.data ); }) }; btns[1 ].onclick = function ( ) { cancel (); };
完善
用户多次点击发送请求,势必会给服务器带来处理压力,因此当用户多次点击发送请求时,检查上次请求是否已经得到响应,如果没有,则取消上次请求。
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" );let cancel = null ;btns[0 ].onclick = function ( ) { if (cancel !== null ) { cancel (); } axios ({ method : "GET" , url : "http://localhost:3000/posts" , cancelToken : new axios.CancelToken (function (c ) { cancel = c; }) }).then (response => { console .log (response.data ); cancel = null ; }) }; btns[1 ].onclick = function ( ) { cancel (); };
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
从 localStorage
中获取 token 和 refresh token。
如果 token 存在,将其添加到请求头中。
检查 token 是否即将过期,如果即将过期则使用 refresh token 刷新 token。
将新的 token 存储在 localStorage
中,并更新请求头。
如果刷新 token 失败,清除 localStorage
中的 token 和 refresh token,并重定向到登录页面。
配置响应拦截:通常用于处理服务器返回的响应数据和错误。
处理响应数据 :解析并返回实际需要的数据部分。
处理错误响应 :根据不同的 HTTP 状态码进行相应的错误处理。
全局错误提示 :统一处理错误提示,例如显示错误消息。
自动刷新 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' ;const instance = axios.create ({ baseURL : 'https://api.example.com' , timeout : 10000 , headers : { 'Content-Type' : 'application/json' , }, }); instance.interceptors .request .use ( async config => { let token = localStorage .getItem ('token' ); const refreshToken = localStorage .getItem ('refreshToken' ); if (token) { config.headers .Authorization = `Bearer ${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); localStorage .removeItem ('token' ); localStorage .removeItem ('refreshToken' ); window .location .href = '/login' ; } } return config; }, error => { return Promise .reject (error); } ); function isTokenExpiringSoon = (token ) => { const payload = JSON .parse (atob (token.split ('.' )[1 ])); const exp = payload.exp * 1000 ; const now = Date .now (); return exp - now < 60000 ; } instance.interceptors .response .use ( response => { return response.data ; }, async error => { if (error.response ) { switch (error.response .status ) { case 401 : console .error ('未授权,请登录' ); 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); localStorage .removeItem ('token' ); localStorage .removeItem ('refreshToken' ); window .location .href = '/login' ; } } else { window .location .href = '/login' ; } break ; case 403 : console .error ('禁止访问' ); break ; case 404 : console .error ('资源未找到' ); break ; default : console .error ('其他错误' , error.response .status ); } } else { console .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 需要安装以下几个库:
Axios :用于 HTTP 请求。
TypeScript :用于类型检查和编译。
@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 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 ) => { 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); } ); } 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); } } 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 import httpService from '@/utils/request' ;import { ResultData } from '@/utils/result' ;interface UserResponse { id : number ; name : string ; email : string ; } 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 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>
著作権表示: 此文章版权归Kisugi Takumi所有,如有转载,请注明来自原作者