当前位置:   article > 正文

axios 请求封装及多个请求loading管理

请求封装

什么是 axios ?

基于 Promise 网络请求库

  • 同构:同一套代码可以运行在浏览器和 node.js 中
  • 在服务端中,使用原生 node.js 的 http 模块
  • 在客户端(浏览器)中,使用 XMLHttpRequest

特性

  • 支持 Promise API
  • 拦截请求和响应
  • 取消请求
  • 自动转换 JSON 数据

一、安装 axios

npm i -S axios qs

二、axios 请求二次封装

src/api/http.js: 

  1. import Vue from 'vue';
  2. import axios from 'axios';
  3. import qs from 'qs';
  4. import router from '../router/index';
  5. let loadingService = null,
  6. loadingRequestQueue = [];
  7. // 默认接口配置
  8. let initConfig = {
  9. isNeedLoading: true, // 是否需要loading
  10. isHideErrorAlert: false, // 是否不显示错误弹窗
  11. };
  12. const instance = axios.create({
  13. timeout: 40000,
  14. });
  15. // post默认请求头设置
  16. instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  17. // 请求拦截
  18. instance.interceptors.request.use(
  19. config => {
  20. // console.log('request--->', config);
  21. const { method = 'get' } = config;
  22. // config.headers.TrackId = config.trackId;
  23. // config.headers.CompanyId = 1693;
  24. if (method === 'post') {
  25. if (!config.data) {
  26. config.data = {};
  27. }
  28. if (config.submitType === 'json') {
  29. config.headers['Content-Type'] = 'application/json';
  30. // config.data = JSON.stringify(config.data) // axios内部自动会序列化
  31. } else {
  32. config.data = qs.stringify(config.data);
  33. }
  34. }
  35. return config;
  36. },
  37. error => {
  38. console.log('request error--->', error);
  39. return Promise.reject(error);
  40. }
  41. );
  42. // 响应拦截器
  43. instance.interceptors.response.use(
  44. response => {
  45. // console.log('response--->', response);
  46. if (response.status === 200) {
  47. return Promise.resolve(response);
  48. }
  49. return Promise.reject(response);
  50. },
  51. error => {
  52. // console.log('errorRes---->', error.response);
  53. // console.log('errorMsg---->', error.message);
  54. const Alert = Vue.prototype.$alert;
  55. const { response, message = '' } = error;
  56. // 在这里可以做个收集错误日志操作
  57. if (response && !response.config?.isHideErrorAlert) {
  58. if (message.indexOf('timeout') > -1) {
  59. Alert('请求超时请重试', '异常信息', {
  60. confirmButtonText: '确定',
  61. type: 'error',
  62. });
  63. }
  64. if (message.indexOf('Network Error') > -1) {
  65. Alert('网络出错', '异常信息', {
  66. confirmButtonText: '确定',
  67. type: 'error',
  68. });
  69. }
  70. switch (response.status) {
  71. case 400:
  72. Alert('400 请求参数有误', '异常信息', {
  73. confirmButtonText: '确定',
  74. type: 'error',
  75. });
  76. break;
  77. // 401: 未登录
  78. // 未登录则跳转登录页面,并携带当前页面的路径
  79. // 在登录成功后返回当前页面,这一步需要在登录页操作。
  80. case 401:
  81. Alert({
  82. message: '未登录! ',
  83. type: 'warning',
  84. });
  85. router.replace({
  86. path: '/login',
  87. query: {
  88. redirect: router.currentRoute.fullPath,
  89. },
  90. });
  91. break;
  92. // 403 登录过期
  93. // 登录过期对用户进行提示
  94. // 清除本地token和清空vuex中token对象
  95. // 跳转登录页面
  96. case 403:
  97. Alert({
  98. message: '登录过期,请重新登录',
  99. type: 'warning',
  100. });
  101. setTimeout(() => {
  102. router.replace({
  103. path: '/login',
  104. query: {
  105. redirect: router.currentRoute.fullPath,
  106. },
  107. });
  108. }, 1000);
  109. break;
  110. case 404:
  111. Alert(response.config?.url + ' 404 Not Found', '异常信息', {
  112. confirmButtonText: '确定',
  113. type: 'error',
  114. });
  115. break;
  116. case 405:
  117. Alert(response.config?.url + ' 405 请求未被允许', '异常信息', {
  118. confirmButtonText: '确定',
  119. type: 'error',
  120. });
  121. break;
  122. case 500:
  123. Alert('500 服务器异常', '异常信息', {
  124. confirmButtonText: '确定',
  125. type: 'error',
  126. });
  127. break;
  128. case 501:
  129. Alert('501 服务器不支持当前请求', '异常信息', {
  130. confirmButtonText: '确定',
  131. type: 'error',
  132. });
  133. break;
  134. case 502:
  135. Alert('502 网关出错', '异常信息', {
  136. confirmButtonText: '确定',
  137. type: 'error',
  138. });
  139. break;
  140. case 503:
  141. Alert('503 服务不可用', '异常信息', {
  142. confirmButtonText: '确定',
  143. type: 'error',
  144. });
  145. break;
  146. case 504:
  147. Alert('504 网关超时', '异常信息', {
  148. confirmButtonText: '确定',
  149. type: 'error',
  150. });
  151. break;
  152. default:
  153. Alert('网络出错', '异常信息', {
  154. confirmButtonText: '确定',
  155. type: 'error',
  156. });
  157. break;
  158. }
  159. }
  160. return Promise.reject(error);
  161. }
  162. );
  163. /**
  164. * 判断是否有接口还未加载完
  165. * @param {*} trackId
  166. */
  167. function compareLoadingStatus(trackId) {
  168. const targetIndex = loadingRequestQueue.findIndex(item => item === trackId);
  169. if (targetIndex > -1) {
  170. loadingRequestQueue.splice(targetIndex, 1);
  171. }
  172. if (!loadingRequestQueue.length) {
  173. loadingService?.close();
  174. }
  175. }
  176. function getTrackId() {
  177. return `trackid${new Date().getTime()}_${(Math.random() * 100000).toFixed(0)}`;
  178. }
  179. // 请求公共函数
  180. function send(method = 'get', url, data = {}, options = {}) {
  181. const Loading = Vue.prototype.$loading;
  182. options = {
  183. ...initConfig,
  184. ...options,
  185. trackId: getTrackId(),
  186. };
  187. if (options.isNeedLoading) {
  188. loadingRequestQueue.push(options.trackId);
  189. if (!loadingService) {
  190. loadingService = Loading?.({
  191. fullscreen: true,
  192. background: 'transparent',
  193. });
  194. }
  195. }
  196. return new Promise((resolve, reject) => {
  197. instance({
  198. method,
  199. url,
  200. [method === 'get' ? 'params' : 'data']: data,
  201. ...options,
  202. })
  203. .then(res => {
  204. resolve(res.data);
  205. })
  206. .catch(error => {
  207. reject(error);
  208. })
  209. .finally(() => {
  210. if (options.isNeedLoading) {
  211. compareLoadingStatus(options.trackId);
  212. }
  213. });
  214. });
  215. }
  216. /**
  217. * 封装get请求
  218. */
  219. export function get(url, params = {}, options = {}) {
  220. return send('get', url, params, options);
  221. }
  222. /**
  223. * 封装post请求
  224. */
  225. export function post(url, data = {}, options = {}) {
  226. return send('post', url, data, options);
  227. }

Loading 和 Alert 用的是 element-ui 的。 

options 可选参数:

  • isNeedLoading: boolean 是否需要loading
  • isHideErrorAlert: boolean 请求错误时是否关闭默认的错误提示弹窗
  • submitType: string,值为 json,请求参数会用 json 格式
  • axios 规定的请求配置,如 timeoutwithCredentials

特有功能:

  • 同时发起多个请求时,所有请求结束后才关闭 loading。

请求 headers 里的 Content-Type 可以不用设置,axios 会根据传入的 data 参数的格式自动设置 Content-Type:

data参数格式Content-Type
"name=jim&age=22" 这种序列化过的格式application/x-www-form-urlencoded
普通对象application/json

三、接口管理

api接口管理的一个好处就是,我们把api统一集中起来,如果后期需要修改接口,我们就直接在api.js中找到对应的修改就好了,而不用去每一个页面查找我们的接口然后再修改会很麻烦。关键是,万一修改的量比较大,就gg了。还有就是如果直接在我们的业务代码修改接口,一不小心还容易动到我们的业务代码造成不必要的麻烦。

  1. // api.js
  2. import { get, post } from './http'
  3. export const apiAddress = parmas => post('api/v1/users/info', parmas);

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号