可以查看axios的中文文档,地址如下
https://www.axios-http.cn/

为什么要对axios进行二次封装:

由于自带的axios不能满足我们的需求,需要对其进行二次封装,方便程序员使用

  1. // 引入axios
  2. import axios from 'axios'
  3. // @ts-ignore
  4. import { ElMessage } from "element-plus";
  5. import (GET_TOKEN) from './token.ts
  6. // 第一步:利用axios对象的create方法,去创建axios实例(其他的配置:基础路径、超时时间)
  7. const requeset = axios.create({
  8. // 基础路径
  9. baseURL: import.meta.env.VITE_APP_BASE_API,//后期可以换成自己的项目地址
  10. // 设置超时时间
  11. timeout: 5000,
  12. })
  13. // // 第二步:给request实例添加请求拦截器
  14. requeset.interceptors.request.use((config)=>{
  15. //获取token
  16. const token = GET_TOKEN()
  17. if(token){
  18. config.header.token = token
  19. }
  20. // 返回配置对象
  21. return config;
  22. })
  23. // 第三步:给request实例添加响应拦截器
  24. requeset.interceptors.response.use((response)=>{
  25. // 成功的回调
  26. // 简化数据
  27. return response.data
  28. },
  29. (error)=>{
  30. // 失败的回调:处理http网络错误
  31. // 定义一个变量,存储网络错误信息
  32. let message = '';
  33. // http状态码
  34. let status = error.response.status
  35. switch(status){
  36. case 401:
  37. message = 'TOKEN过期';
  38. break;
  39. case 403:
  40. message = '无权访问';
  41. break;
  42. case 404:
  43. message = '请求地址错误';
  44. break;
  45. case 500:
  46. message = '服务器出现问题';
  47. break;
  48. default:
  49. message = '网络出现错误';
  50. break;
  51. }
  52. // 提示错误信息
  53. ElMessage({
  54. type:'error',
  55. message
  56. })
  57. return Promise.reject(error);
  58. }
  59. )
  60. // 对外暴露
  61. export default requeset;

而在开发阶段,我们可以使用上面对axios的二次封装来发送请求获取数据,在生产环境中为了提高用户的体验,减小网络带宽,减小服务器压力,我们会在发请求前对用户的地址进行判断,对网络的错误进行统一的错误提示
1.统一错误处理

  1. import axios from "axios";
  2. import { ElMessage } from "element-plus";
  3. import { useUserStore } from "../../store/modules/user";
  4. // 功能失败的错误原因
  5. export const authErrMessage: any = {
  6. 10031: "登录失效,需要重新登录",
  7. 10032: "您太久没登录,请重新登录~",
  8. 10033: "账户未绑定角色,请联系管理员绑定角色",
  9. 10034: "该用户未注册,请联系管理员注册用户",
  10. 10035: "code 无法获取对应第三方平台用户",
  11. 10036: "该账户未关联员工,请联系管理员做关联",
  12. 10037: "账号已无效",
  13. 10038: "账号未找到",
  14. };
  15. // 请求失败的错误原因
  16. export const networkErrMessage: any = {
  17. 400: "错误的请求",
  18. 401: "未授权,请重新登录",
  19. 403: "拒绝访问",
  20. 404: "未找到该资源",
  21. 405: "请求方法未允许",
  22. 408: "请求超时",
  23. 500: "服务器端出错",
  24. 502: "网络错误",
  25. 503: "服务不可用",
  26. 504: "网络超时",
  27. 505: "http版本不支持该请求",
  28. };
  29. const request = axios.create({
  30. // 需要在 .env.* 文件中定义环境变量才能使用
  31. baseURL: import.meta.env.VITE_BASE_URL,
  32. headers: {},
  33. timeout: 20000,
  34. });
  35. request.interceptors.request.use((config) => {
  36. const token = useUserStore().token;
  37. if (token) {
  38. config.headers.token = token;
  39. }
  40. return config;
  41. });
  42. request.interceptors.response.use(
  43. (response) => {
  44. const code = response.data.code;
  45. if (code === 20000) {
  46. return response.data;
  47. } else {
  48. let errMessage = authErrMessage[code];
  49. if (errMessage) {
  50. // 功能出错,需要特殊处理,比如要退出登录等
  51. // useUserStore().logout();
  52. } else {
  53. errMessage = response.data.message;
  54. }
  55. ElMessage.error(errMessage);
  56. return Promise.reject(errMessage);
  57. }
  58. },
  59. (error) => {
  60. let errMessage = "未知错误,请联系管理员解决";
  61. if (error.response) {
  62. // error.response有值,说明服务器有响应,响应结果是错误的,要根据响应状态码error.response.status来提示错误
  63. errMessage = networkErrMessage[error.response.status];
  64. } else {
  65. // error.response没有值,说明服务器没有响应,请求在客户端就失败了,要根据浏览器提示的错误信息来处理
  66. if (error.message.indexOf("timeout") > -1) {
  67. // 请求超时
  68. errMessage = "当前网络环境不稳定,请切换4/5G网络或WIFI网络试试";
  69. } else if (error.message.indexOf("Network") > -1) {
  70. // 断网了
  71. errMessage = "没有检测到网络,请打开网络链接试试";
  72. } else if (error.message.indexOf("canceled") > -1) {
  73. errMessage = "请求取消了";
  74. }
  75. }
  76. ElMessage.error(errMessage);
  77. return Promise.reject(errMessage);
  78. }
  79. );
  80. export default request;

2.取消重复请求
每个请求根据请求的配置项生成一个相应key(相同的请求生成的key相同,不同请求生成的key不同)
1.生成key

  1. // 所有请求列表容器
  2. //每个请求生成的key及key对应的值cancel
  3. const requestListMap = new Map();
  4. /**
  5. * 每一个请求生成一个key
  6. * @param config 请求配置对象
  7. * @returns 唯一的key
  8. */
  9. function getRequestKey(config: AxiosRequestConfig) {
  10. let { url, method, params = {}, data = {} } = config;
  11. return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
  12. }

删除key

  1. /**
  2. * 删除请求key
  3. * @param config
  4. */
  5. function removeRequestKey(config: AxiosRequestConfig) {
  6. const requestKey = getRequestKey(config);
  7. if (requestListMap.has(requestKey)) {
  8. const cancel = requestListMap.get(requestKey);
  9. cancel(); // 取消请求
  10. requestListMap.delete(requestKey);
  11. }
  12. }

添加key

  1. const CancelToken = axios.CancelToken;
  2. /**
  3. * 添加请求key
  4. * @param config
  5. */
  6. function addRequestKey(config: AxiosRequestConfig) {
  7. const requestKey = getRequestKey(config);
  8. config.cancelToken = new CancelToken((cancel) => {
  9. if (!requestListMap.has(requestKey)) {
  10. requestListMap.set(requestKey, cancel);
  11. }
  12. });
  13. }

请求完成,在响应拦截器中需要将当前请求给删除

  1. import axios from "axios";
  2. import type { AxiosRequestConfig } from "axios";
  3. import { ElMessage } from "element-plus";
  4. import { useUserStore } from "../store/modules/user";
  5. // 功能失败的错误原因
  6. export const authErrMessage: any = {
  7. 10031: "登录失效,需要重新登录",
  8. 10032: "您太久没登录,请重新登录~",
  9. 10033: "账户未绑定角色,请联系管理员绑定角色",
  10. 10034: "该用户未注册,请联系管理员注册用户",
  11. 10035: "code 无法获取对应第三方平台用户",
  12. 10036: "该账户未关联员工,请联系管理员做关联",
  13. 10037: "账号已无效",
  14. 10038: "账号未找到",
  15. };
  16. // 请求失败的错误原因
  17. export const networkErrMessage: any = {
  18. 400: "错误的请求",
  19. 401: "未授权,请重新登录",
  20. 403: "拒绝访问",
  21. 404: "未找到该资源",
  22. 405: "请求方法未允许",
  23. 408: "请求超时",
  24. 500: "服务器端出错",
  25. 502: "网络错误",
  26. 503: "服务不可用",
  27. 504: "网络超时",
  28. 505: "http版本不支持该请求",
  29. };
  30. // 所有请求列表容器
  31. const requestListMap = new Map();
  32. /**
  33. * 每一个请求生成一个唯一的key
  34. * @param config 请求配置对象
  35. * @returns 唯一的key
  36. */
  37. function getRequestKey(config: AxiosRequestConfig) {
  38. const { url, method, params = {}, data = {} } = config;
  39. return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
  40. }
  41. const CancelToken = axios.CancelToken;
  42. /**
  43. * 添加请求key
  44. * @param config
  45. */
  46. function addRequestKey(config: AxiosRequestConfig) {
  47. const requestKey = getRequestKey(config);
  48. config.cancelToken = new CancelToken((cancel) => {
  49. if (!requestListMap.has(requestKey)) {
  50. requestListMap.set(requestKey, cancel);
  51. }
  52. });
  53. }
  54. /**
  55. * 删除请求key
  56. * @param config
  57. */
  58. function removeRequestKey(config: AxiosRequestConfig) {
  59. const requestKey = getRequestKey(config);
  60. if (requestListMap.has(requestKey)) {
  61. const cancel = requestListMap.get(requestKey);
  62. cancel();
  63. requestListMap.delete(requestKey);
  64. }
  65. }
  66. const request = axios.create({
  67. // 需要在 .env.* 文件中定义环境变量才能使用
  68. baseURL: import.meta.env.VITE_BASE_URL,
  69. headers: {},
  70. timeout: 20000,
  71. });
  72. request.interceptors.request.use((config) => {
  73. const token = useUserStore().token;
  74. if (token) {
  75. config.headers.token = token;
  76. }
  77. removeRequestKey(config); // 删除key
  78. addRequestKey(config); // 添加key
  79. return config;
  80. });
  81. request.interceptors.response.use(
  82. (response) => {
  83. removeRequestKey(response.config); // 删除key
  84. const code = response.data.code;
  85. if (code === 20000) {
  86. return response.data;
  87. } else {
  88. let errMessage = authErrMessage[code];
  89. if (errMessage) {
  90. // 功能出错,需要特殊处理,比如要退出登录等
  91. // useUserStore().logout();
  92. } else {
  93. errMessage = response.data.message;
  94. }
  95. ElMessage.error(errMessage);
  96. return Promise.reject(errMessage);
  97. }
  98. },
  99. (error) => {
  100. error.config && removeRequestKey(error.config); // 删除key
  101. let errMessage = "未知错误,请联系管理员解决";
  102. if (error.response) {
  103. // error.response有值,说明服务器有响应,响应结果是错误的,要根据响应状态码error.response.status来提示错误
  104. errMessage = networkErrMessage[error.response.status];
  105. } else {
  106. // error.response没有值,说明服务器没有响应,请求在客户端就失败了,要根据浏览器提示的错误信息来处理
  107. if (error.message.indexOf("timeout") > -1) {
  108. // 请求超时
  109. errMessage = "当前网络环境不稳定,请切换4/5G网络或WIFI网络试试";
  110. } else if (error.message.indexOf("Network") > -1) {
  111. // 断网了
  112. errMessage = "没有检测到网络,请打开网络链接试试";
  113. } else if (error.message.indexOf("canceled") > -1) {
  114. errMessage = "请求取消了";
  115. }
  116. }
  117. ElMessage.error(errMessage);
  118. return Promise.reject(errMessage);
  119. }
  120. );
  121. export default request;

取消上一个页面的请求

  1. 存储请求时,需要将取消请求的方法和当前路由路径一起储存
  2. 当路由跳转时,判断请求列表中的请求地址和要去的路由地址是否时同一个,如果不是,就要取消 ```typescript import axios from “axios”; import type { AxiosRequestConfig } from “axios”; import { ElMessage } from “element-plus”; import { useUserStore } from “../store/modules/user”;

// 功能失败的错误原因 export const authErrMessage: any = { 10031: “登录失效,需要重新登录”, 10032: “您太久没登录,请重新登录~”, 10033: “账户未绑定角色,请联系管理员绑定角色”, 10034: “该用户未注册,请联系管理员注册用户”, 10035: “code 无法获取对应第三方平台用户”, 10036: “该账户未关联员工,请联系管理员做关联”, 10037: “账号已无效”, 10038: “账号未找到”, };

// 请求失败的错误原因 export const networkErrMessage: any = { 400: “错误的请求”, 401: “未授权,请重新登录”, 403: “拒绝访问”, 404: “未找到该资源”, 405: “请求方法未允许”, 408: “请求超时”, 500: “服务器端出错”, 502: “网络错误”, 503: “服务不可用”, 504: “网络超时”, 505: “http版本不支持该请求”, };

// 所有请求列表容器 const requestListMap = new Map();

/**

  • 每一个请求生成一个唯一的key
  • @param config 请求配置对象
  • @returns 唯一的key */ function getRequestKey(config: AxiosRequestConfig) { const { url, method, params = {}, data = {} } = config; return [url, method, JSON.stringify(params), JSON.stringify(data)].join(“&”); }

const CancelToken = axios.CancelToken;

/**

  • 添加请求key
  • @param config */ function addRequestKey(config: AxiosRequestConfig) { const requestKey = getRequestKey(config); config.cancelToken = new CancelToken((cancel) => { if (!requestListMap.has(requestKey)) { requestListMap.set(requestKey, {
    1. cancel,
    2. pathname: window.location.pathname,
    }); } }); }

/**

  • 删除请求key
  • @param config */ function removeRequestKey(config: AxiosRequestConfig) { const requestKey = getRequestKey(config); if (requestListMap.has(requestKey)) { const { cancel } = requestListMap.get(requestKey); cancel(); requestListMap.delete(requestKey); } }

const request = axios.create({ // 需要在 .env.* 文件中定义环境变量才能使用 baseURL: import.meta.env.VITE_BASE_URL, headers: {}, timeout: 20000, });

request.interceptors.request.use((config) => { const token = useUserStore().token; if (token) { config.headers.token = token; } removeRequestKey(config); // 删除key addRequestKey(config); // 添加key return config; });

request.interceptors.response.use( (response) => { removeRequestKey(response.config); // 删除key const code = response.data.code; if (code === 20000) { return response.data; } else { let errMessage = authErrMessage[code]; if (errMessage) { // 功能出错,需要特殊处理,比如要退出登录等 // useUserStore().logout(); } else { errMessage = response.data.message; } ElMessage.error(errMessage); return Promise.reject(errMessage); } }, (error) => { error.config && removeRequestKey(error.config); // 删除key let errMessage = “未知错误,请联系管理员解决”; if (error.response) { // error.response有值,说明服务器有响应,响应结果是错误的,要根据响应状态码error.response.status来提示错误 errMessage = networkErrMessage[error.response.status]; } else { // error.response没有值,说明服务器没有响应,请求在客户端就失败了,要根据浏览器提示的错误信息来处理 if (error.message.indexOf(“timeout”) > -1) { // 请求超时 errMessage = “当前网络环境不稳定,请切换4/5G网络或WIFI网络试试”; } else if (error.message.indexOf(“Network”) > -1) { // 断网了 errMessage = “没有检测到网络,请打开网络链接试试”; } else if (error.message.indexOf(“canceled”) > -1) { errMessage = “请求取消了”; } } ElMessage.error(errMessage); return Promise.reject(errMessage); } );

export { requestListMap };

export default request;

  1. <a name="lGn8D"></a>
  2. ## Axios的二次封装最终版
  3. ```typescript
  4. import axios from "axios";
  5. import type { AxiosRequestConfig } from "axios";
  6. import { ElMessage } from "element-plus";
  7. import { useUserStore } from "../store/modules/user";
  8. // 功能失败的错误原因
  9. export const authErrMessage: any = {
  10. 10031: "登录失效,需要重新登录",
  11. 10032: "您太久没登录,请重新登录~",
  12. 10033: "账户未绑定角色,请联系管理员绑定角色",
  13. 10034: "该用户未注册,请联系管理员注册用户",
  14. 10035: "code 无法获取对应第三方平台用户",
  15. 10036: "该账户未关联员工,请联系管理员做关联",
  16. 10037: "账号已无效",
  17. 10038: "账号未找到",
  18. };
  19. // 请求失败的错误原因
  20. export const networkErrMessage: any = {
  21. 400: "错误的请求",
  22. 401: "未授权,请重新登录",
  23. 403: "拒绝访问",
  24. 404: "未找到该资源",
  25. 405: "请求方法未允许",
  26. 408: "请求超时",
  27. 500: "服务器端出错",
  28. 502: "网络错误",
  29. 503: "服务不可用",
  30. 504: "网络超时",
  31. 505: "http版本不支持该请求",
  32. };
  33. // 所有请求列表容器
  34. const requestListMap = new Map();
  35. /**
  36. * 每一个请求生成一个唯一的key
  37. * @param config 请求配置对象
  38. * @returns 唯一的key
  39. */
  40. function getRequestKey(config: AxiosRequestConfig) {
  41. const { url, method, params = {}, data = {} } = config;
  42. return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
  43. }
  44. const CancelToken = axios.CancelToken;
  45. /**
  46. * 添加请求key
  47. * @param config
  48. */
  49. function addRequestKey(config: AxiosRequestConfig) {
  50. const requestKey = getRequestKey(config);
  51. config.cancelToken = new CancelToken((cancel) => {
  52. if (!requestListMap.has(requestKey)) {
  53. requestListMap.set(requestKey, {
  54. cancel,
  55. pathname: window.location.pathname,
  56. });
  57. }
  58. });
  59. }
  60. /**
  61. * 删除请求key
  62. * @param config
  63. */
  64. function removeRequestKey(config: AxiosRequestConfig) {
  65. const requestKey = getRequestKey(config);
  66. if (requestListMap.has(requestKey)) {
  67. const { cancel } = requestListMap.get(requestKey);
  68. cancel();
  69. requestListMap.delete(requestKey);
  70. }
  71. }
  72. const request = axios.create({
  73. // 需要在 .env.* 文件中定义环境变量才能使用
  74. baseURL: import.meta.env.VITE_BASE_URL,
  75. headers: {},
  76. timeout: 20000,
  77. });
  78. request.interceptors.request.use((config) => {
  79. const token = useUserStore().token;
  80. if (token) {
  81. config.headers.token = token;
  82. }
  83. removeRequestKey(config); // 删除key
  84. addRequestKey(config); // 添加key
  85. return config;
  86. });
  87. request.interceptors.response.use(
  88. (response) => {
  89. removeRequestKey(response.config); // 删除key
  90. const code = response.data.code;
  91. if (code === 20000) {
  92. return response.data;
  93. } else {
  94. let errMessage = authErrMessage[code];
  95. if (errMessage) {
  96. // 功能出错,需要特殊处理,比如要退出登录等
  97. // useUserStore().logout();
  98. } else {
  99. errMessage = response.data.message;
  100. }
  101. ElMessage.error(errMessage);
  102. return Promise.reject(errMessage);
  103. }
  104. },
  105. (error) => {
  106. error.config && removeRequestKey(error.config); // 删除key
  107. let errMessage = "未知错误,请联系管理员解决";
  108. if (error.response) {
  109. // error.response有值,说明服务器有响应,响应结果是错误的,要根据响应状态码error.response.status来提示错误
  110. errMessage = networkErrMessage[error.response.status];
  111. } else {
  112. // error.response没有值,说明服务器没有响应,请求在客户端就失败了,要根据浏览器提示的错误信息来处理
  113. if (error.message.indexOf("timeout") > -1) {
  114. // 请求超时
  115. errMessage = "当前网络环境不稳定,请切换4/5G网络或WIFI网络试试";
  116. } else if (error.message.indexOf("Network") > -1) {
  117. // 断网了
  118. errMessage = "没有检测到网络,请打开网络链接试试";
  119. } else if (error.message.indexOf("canceled") > -1) {
  120. errMessage = "请求取消了";
  121. }
  122. }
  123. ElMessage.error(errMessage);
  124. return Promise.reject(errMessage);
  125. }
  126. );
  127. export { requestListMap };
  128. export default request;

开发环境的判断,需要安装第三方包

  1. pnpm i @type/node