1:什么是axios

axios是一种实现ajax的方式,基于promise。实现ajax的方式有多种,如jQuery封装的ajax,原生的XMLHttpRequest,以及axios。
Axios,可以理解为ajax i/o system,这不是一种新技术,本质上还是对原生XMLHttpRequest的封装,可用于浏览器和nodejs的HTTP客户端,只不过它是基于Promise的,符合最新的ES规范。具备以下特点:

  1. 在浏览器中创建XMLHttpRequest请求
  2. 在node.js中发送http请求
  3. 支持Promise API
  4. 拦截请求和响应
  5. 转换请求和响应数据
  6. 取消要求
  7. 自动转换JSON数据
  8. 客户端支持防止CSRF/XSRF(跨域请求伪造)

    2: axios的请求方式:

  • axios(config)
  • axios.request(config)
  • axios.get(url [,config])
  • axios.post(url [,data [,config]])
  • axios.put(url [,data [,config]])
  • axios.delete(url [,config])
  • axios.patch(url [,data [,config]])
  • axios.head(url [,config])

    3:发送并发请求

    通过axios.all(iterable)可实现发送多个请求,参数不一定是数组,只要有iterable接口就行,函数返回的是一个数组。axios.spread(callback)可用于将结果数组展开。
    1. //发送多个请求(并发请求),类似于promise.all,若一个请求出错,那就会停止请求
    2. const get1 = axios.get('/user/12345');
    3. const get2 = axios.get('/user/12345/permission');
    4. axios.all([get1,get2])
    5. .then(axios.spread((res1,res2)=>{
    6. console.log(res1,res2);
    7. }))
    8. .catch(err=>console.log(err))

4:axios API

axios(config)可通过设置一些属性来发送请求

  1. //发送post请求
  2. axios({
  3. method: 'post', //请求方式,默认是get请求
  4. url:'/user/12345', //地址
  5. data:{ //参数
  6. firstName: 'simon',
  7. lastName: 'li'
  8. }
  9. });

5:全局配置

  1. axios.all([axios({
  2. url:'http://123.207.32.32:8000/home/multidata'
  3. }),axios({
  4. url:'http://123.207.32.32:8000/home/data',
  5. params:{
  6. type:'sell',
  7. page:4
  8. }
  9. })]).then(results=>{
  10. console.log(results);
  11. console.log(results[0]);
  12. console.log(results[1]);
  13. })

在上面的例子里,BaseURL是固定的,可以进行一部分抽取,也可以利用axiox的全局配置。

  1. // 全局配置
  2. // 将共有的域名定义为全局配置,使得代码更加简洁
  3. axios.defaults.baseURL ='http://123.207.32.32:8000'
  4. axios.defaults.timeout = 5000
  5. axios.all([axios({
  6. url:'/home/multidata'
  7. }),axios({
  8. url:'/home/data',
  9. params:{
  10. type:'sell',
  11. page:4
  12. }
  13. })]).then(axios.spread((res1,res2)=>{
  14. console.log(res1);
  15. console.log(res2);
  16. }))

常用的配置选项
20201210151719118.png
但是如果直接设置全局配置的话,那么当某个请求的baseURL等其他信息并不是这个的时候,却没办法灵活应对。这个时候就需要创建axios的实例

6:axios的实例

为什么要创建axios的实例呢?

  • 当我们从axios模块中导入对象时, 使用的实例是默认的实例.
  • 当给该实例设置一些默认配置时, 这些配置就被固定下来了.
  • 但是后续开发中, 某些配置可能会不太一样.
  • 比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.
  • 这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.

演示如下:通过多个实例实现配置的互不干涉。

  1. // 4.创建axios的实例
  2. const instance1 = axios.create({
  3. baseURL:'http://123.207.32.32:8000',
  4. timeout:5000
  5. })
  6. // 进行一次网络请求
  7. instance1({
  8. url:'/home/multidata'
  9. }).then(res=>{
  10. console.log(res);
  11. })
  12. instance1({
  13. url:'/home/data',
  14. params:{
  15. type:'pop',
  16. page:1
  17. }
  18. }).then(res =>{
  19. console.log(res);
  20. })
  21. // 从而实现每一个axios实例有自己独立的配置
  22. const instance2 = axios.create({
  23. baseURL:'http://123.123.312.132:8000',
  24. timeout:10000,
  25. })

7:axios框架模块封装

如果不进行封装,那么在每一次要进行网络请求的时候,我们都是单独的书写对于这个axios框架下的对应代码及其代码思路,但如果某一天,使用的这个框架出现大漏洞或者不再维护,我们不得不改用其他框架,那程序员就必须在每一个使用了axios框架的地方进行代码的修改,工作量极大。
因此,对axios进行模块封装,可以使得后面维护的成本减小。
例子:

  1. import axios, { Canceler, AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios';
  2. import { ElMessage } from 'element-plus';
  3. import qs from 'qs';
  4. import pinia, { useStore } from '@/stores';
  5. import { useAxiosRequestStore } from '@/stores/axiosRequest';
  6. import './loginInterceptor';
  7. import { baseUrl, SmartResponse } from './config';
  8. // 携带cookie信息
  9. axios.defaults.withCredentials = true;
  10. // 设置defaults.baseURL
  11. axios.defaults.baseURL = baseUrl;
  12. // request 拦截器
  13. axios.interceptors.request.use(
  14. (config: AxiosRequestConfig) => {
  15. // vuex记录cancelToken
  16. const axiosRequestStore = useAxiosRequestStore(pinia);
  17. config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
  18. axiosRequestStore.pushToken({ cancelToken: cancel });
  19. });
  20. return config;
  21. },
  22. (error: any) => Promise.reject(error),
  23. );
  24. // response 拦截器 请求超时拦截,重新请求
  25. axios.interceptors.response.use(
  26. (response: AxiosResponse) => {
  27. const { status } = response;
  28. if (status !== 200) {
  29. ElMessage({
  30. type: 'error',
  31. message: `请求服务好像出错了,${status}`,
  32. });
  33. return Promise.reject(response);
  34. }
  35. return Promise.resolve(response);
  36. },
  37. (err: any) => {
  38. const { config } = err;
  39. if (err.message === 'interrupt') {
  40. // 判断是否为路由跳转取消网络请求
  41. console.warn('路由跳转取消请求');
  42. return Promise.reject(err);
  43. } else {
  44. // 全局的请求次数,请求的间隙
  45. const [RETRY_COUNT, RETRY_DELAY] = [2, 2000];
  46. if (config && RETRY_COUNT) {
  47. // 设置用于跟踪重试计数的变量
  48. config.__retryCount = config.__retryCount || 0;
  49. // 检查是否已经把重试的总数用完
  50. if (config.__retryCount >= RETRY_COUNT) {
  51. return Promise.reject(err);
  52. }
  53. // 增加重试计数
  54. // eslint-disable-next-line no-plusplus
  55. config.__retryCount++;
  56. // 创造新的Promise来处理指数后退
  57. const backoff = new Promise<void>((resolve: Function) => {
  58. setTimeout(() => {
  59. resolve();
  60. }, RETRY_DELAY || 1);
  61. });
  62. // instance重试请求的Promise
  63. return backoff.then(() => axios(config));
  64. }
  65. return Promise.reject(err);
  66. }
  67. },
  68. );
  69. const store = useStore(pinia);
  70. export const request = {
  71. /**
  72. * get获取数据,通用方法
  73. */
  74. doGetPromise(url: string, params?: any, options: any = {}) {
  75. const { timeout = 30000, ...arg } = options;
  76. return new Promise<SmartResponse>((resolve, reject) => {
  77. axios
  78. .get(url, {
  79. timeout: timeout,
  80. ...arg,
  81. params: {
  82. systemId: store.platformInfo.systemId, // 全面接口添加systemId字段
  83. userId: store.userInfo.userId,
  84. ...params,
  85. },
  86. })
  87. .then((res: AxiosResponse) => {
  88. resolve(res?.data);
  89. })
  90. .catch((err: AxiosError) => {
  91. reject(err);
  92. });
  93. });
  94. },
  95. /**
  96. * FormData数据上传,文件上传必用
  97. */
  98. doPostPromiseForm(url: string, formData: FormData, params: any = {}) {
  99. return new Promise<SmartResponse>((resolve, reject) => {
  100. axios
  101. .post(url, formData, {
  102. headers: {
  103. 'Content-type': 'multipart/form-data',
  104. },
  105. params: {
  106. userId: store.userInfo.userId,
  107. ...params,
  108. },
  109. })
  110. .then((res: AxiosResponse) => {
  111. resolve(res?.data);
  112. })
  113. .catch((err: AxiosError) => {
  114. reject(err);
  115. });
  116. });
  117. },
  118. /**
  119. * 默认方式提交from表单数据
  120. */
  121. doPostPromise(url: string, data: any, params: any = {}) {
  122. return new Promise<SmartResponse>((resolve, reject) => {
  123. // 全面接口添加systemId字段
  124. if (!params.hasOwnProperty('systemId')) params.systemId = store.platformInfo.systemId;
  125. axios
  126. .post(url, qs.stringify(data), {
  127. headers: {
  128. 'Content-type': 'application/x-www-form-urlencoded',
  129. },
  130. params: {
  131. userId: store.userInfo.userId,
  132. ...params,
  133. },
  134. })
  135. .then((res: AxiosResponse) => {
  136. resolve(res?.data);
  137. })
  138. .catch((err: AxiosError) => {
  139. reject(err);
  140. });
  141. });
  142. },
  143. /**
  144. * 默认方式提交json数据
  145. */
  146. doPostPromiseJson(url: string, data: any, params: any = {}) {
  147. return new Promise<SmartResponse>((resolve, reject) => {
  148. axios
  149. .post(url, data, {
  150. headers: {
  151. 'Content-type': 'application/json',
  152. },
  153. params: {
  154. userId: store.userInfo.userId,
  155. ...params,
  156. },
  157. })
  158. .then((res: AxiosResponse) => {
  159. resolve(res?.data);
  160. })
  161. .catch((err: AxiosError) => {
  162. reject(err);
  163. });
  164. });
  165. },
  166. /**
  167. * Put方式提交json数据
  168. */
  169. doPutPromiseJson(url: string, data: any, params: any = {}) {
  170. return new Promise<SmartResponse>((resolve, reject) => {
  171. axios
  172. .put(url, data, {
  173. headers: {
  174. 'Content-type': 'application/json',
  175. },
  176. params: { ...params },
  177. })
  178. .then((res: AxiosResponse) => {
  179. resolve(res?.data);
  180. })
  181. .catch((err: AxiosError) => {
  182. reject(err);
  183. });
  184. });
  185. },
  186. /**
  187. * Patch方式提交表单数据
  188. */
  189. doPatchPromise(url: string, data: any) {
  190. return new Promise<SmartResponse>((resolve, reject) => {
  191. axios
  192. .patch(url, qs.stringify(data), {
  193. headers: {
  194. 'Content-type': 'application/x-www-form-urlencoded',
  195. },
  196. params: {},
  197. })
  198. .then((res: AxiosResponse) => {
  199. resolve(res?.data);
  200. })
  201. .catch((err: AxiosError) => {
  202. reject(err);
  203. });
  204. });
  205. },
  206. /**
  207. * Patch方式提交json数据
  208. */
  209. doPatchPromiseJson(url: string, data: any, params: any = {}) {
  210. return new Promise<SmartResponse>((resolve, reject) => {
  211. axios
  212. .patch(url, data, {
  213. headers: {
  214. 'Content-type': 'application/json',
  215. },
  216. params: { ...params },
  217. })
  218. .then((res: AxiosResponse) => {
  219. resolve(res?.data);
  220. })
  221. .catch((err: AxiosError) => {
  222. reject(err);
  223. });
  224. });
  225. },
  226. /**
  227. * Delete方式
  228. */
  229. doDeletePromise(url: string, params: any = {}) {
  230. return new Promise<SmartResponse>((resolve, reject) => {
  231. axios
  232. .delete(url, {
  233. headers: {
  234. 'Content-type': 'application/x-www-form-urlencoded',
  235. },
  236. params: {
  237. ...params,
  238. },
  239. })
  240. .then((res: AxiosResponse) => {
  241. resolve(res?.data);
  242. })
  243. .catch((err: AxiosError) => {
  244. reject(err);
  245. });
  246. });
  247. },
  248. };

8:拦截器interceptors

拦截器是指当发送请求或者得到响应被then或catch处理之前对它们进行拦截,拦截后可对数据做一些处理,比如给请求数据添加头部信息,或对响应数据进行序列化,然后再传给浏览器,这些都可以在拦截器中进行

  1. //添加一个请求拦截器
  2. axios.interceptors.request.use(config=>{
  3. //在请求之前做一些事
  4. return config;
  5. },err=>{
  6. //请求错误的时候做一些事
  7. return Promise.reject(err);
  8. });
  9. //添加一个响应拦截器
  10. axios.interceptors.response.use(response=>{
  11. //对返回的数据做一些处理
  12. reutrn response;
  13. },err=>{
  14. //对返回的错误做一些处理
  15. return Promise.reject(err);
  16. });
  17. //移除拦截器
  18. const myInterceptor = axios.interceptors.request.use(config=>{return cofig})
  19. axios.interceptors.request.eject(myInterceptor);
  20. //在一个axios实例中使用拦截器
  21. var instance = axios.create();
  22. instance.interceptors.request.use(function(){/*...*/});

9:Config配置选项

  1. {
  2. //服务器的地址,是必须的选项
  3. url: '/user',
  4. //请求的方式,若没有则默认是get
  5. method:'get',
  6. //如果url不是绝对地址,则会加上baseURL
  7. baseURL: 'http://localhost:3000/',
  8. //transformRequest允许请求的数据在发送至服务器之前进行处理,这个属性只适用于put、post、patch方式
  9. //数组的最后一个函数必须返回一个字符串或者一个'ArrayBuffer'或'Stream'或'Buffer' 实例或'ArrayBuffer','Formdata',
  10. //若函数中用到了headers,则需要设置headers属性
  11. transformRequest: [function(data,headers){
  12. //根据需求对数据进行处理
  13. return data;
  14. }],
  15. //transformResponse允许对返回的数据传入then/catch之前进行处理
  16. transformResponse:[function(data){
  17. //依需要对数据进行处理
  18. return data;
  19. }],
  20. //headers是自定义的要被发送的信息头
  21. headers: {'X-Requested-with':'XMLHttpRequest'},
  22. //params是请求连接中的请求参数,必须是一个纯对象
  23. params:{
  24. ID:12345
  25. },
  26. //paramsSerializer用于序列化参数
  27. paramsSerializer: function(params){
  28. return Qs.stringify(params,{arrayFormat:'brackets'});
  29. },
  30. //data是请求时作为请求体的数据——request.body
  31. //只适用于put、post、patch请求方法
  32. //浏览器:FormData,File,Blob;Node:stream
  33. data:{
  34. firstName: 'simon',
  35. },
  36. //timeout定义请求的时间,单位是毫秒,如果请求时间超过设定时间,请求将停止
  37. timeout:1000,
  38. //withCredentials表明跨跨域请求书否需要证明。
  39. withCredentials:false, //默认值
  40. //adapter适配器,允许自定义处理请求
  41. //返回一个promise
  42. adapter:function(config){
  43. /*...*/
  44. },
  45. //auth表明HTTP基础的认证应该被使用,并提供证书
  46. auth:{
  47. username:'simon',
  48. password:'123456',
  49. },
  50. //responseType表明服务器返回的数据类型,这些类型包括:json/blob/document/ arraybuffer/text/stream
  51. responseType: 'json',
  52. //proxy定义服务器的主机名和端口号
  53. //auth属性表明HTTP基本认证应该跟proxy相连接,并提供证书
  54. //这将设置一个'Proxy-Authorization'头(header),覆盖原来自定义的
  55. proxy:{
  56. host:127.0.0.1,
  57. port:8080,
  58. auth:{
  59. username:'simon',
  60. password:'123456'
  61. }
  62. },
  63. //取消请求
  64. cancelToken: new CancelToken(cancel=>{})
  65. }