经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
关于Vue中axios的封装实例详解
来源:jb51  时间:2019/10/21 8:38:08  对本文有异议

前言

axios 是 Vue 官方推荐的一个 HTTP 库,用 axios 官方简介来介绍它,就是:

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

作为一个优秀的 HTTP 库,axios 打败了曾经由 Vue 官方团队维护的 vue-resource,获得了 Vue 作者尤小右的大力推荐,成为了 Vue 项目中 HTTP 库的最佳选择。

虽然,axios 是个优秀的 HTTP 库,但是,直接在项目中使用并不是那么方便,所以,我们需要对其进行一定程度上的配置封装,减少重复代码,方便调用。下面,我们就来聊聊 Vue 中 axios 的封装。

开始

其实,网上关于 axios 封装的代码不少,但是大部分都是在入口文件(main.js)中进行 axios 全局对象属性定义的形式进行配置,类似于如下代码:

  1. axios.defaults.timeout = 10000

该方案有两个不足,首先,axios 封装代码耦合进入入口文件,不方便后期维护;其次,使用 axios 全局对象属性定义的方式进行配置,代码过于零散。

针对问题一,我使用了 Vue 源码结构中的一大核心思想——将功能拆分为文件,方便后期的维护。单独创建一个 http.js 或者 http.ts 文件,在文件中引入 axios 并对其进行封装配置,最后将其导出并挂载到 Vue 的原型上即可。此时,每次修改 axios 配置,只需要修改对应的文件即可,不会影响到不相关的功能。

针对问题二,采用 axios 官方推荐的,通过配置项创建 axios 实例的方式进行配置封装。

代码如下:

  1. // http.js
  2. import axios from 'axios'
  3. // 创建 axios 实例
  4. const service = axios.create({
  5. // 配置项
  6. })

根据环境设置 baseURL

baseURL 属性是请求地址前缀,将自动加在 url 前面,除非 url 是个绝对地址。正常情况下,在开发环境下和生产模式下有着不同的 baseURL,所以,我们需要根据不同的环境切换不同的 baseURL。

在开发模式下,由于有着 devServer 的存在,需要根据固定的 url 前缀进行请求地址重写,所以,在开发环境下,将 baseURL 设为某个固定的值,比如:/apis。

在生产模式下,根据 Java 模块的请求前缀的不同,可以设置不同的 baseURL。

具体代码如下:

  1. // 根据 process.env.NODE_ENV 区分状态,切换不同的 baseURL
  2. const service = axios.create({
  3. baseURL: process.env.NODE_ENV === 'production' ? `/java` : '/apis',
  4. })

统一设置请求头

在这里和大家聊一个问题,什么是封装?在我看来,封装是通过更少的调用代码覆盖更多的调用场景。

由于,大部分情况下,请求头都是固定的,只有少部分情况下,会需要一些特殊的请求头,所以,在这里,我采用的方案是,将普适性的请求头作为基础配置。当需要特殊请求头时,将特殊请求头作为参数传入,覆盖基础配置。

代码如下:

  1. const service = axios.create({
  2. ...
  3. headers: {
  4. get: {
  5. 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  6. // 在开发中,一般还需要单点登录或者其他功能的通用请求头,可以一并配置进来
  7. },
  8. post: {
  9. 'Content-Type': 'application/json;charset=utf-8'
  10. // 在开发中,一般还需要单点登录或者其他功能的通用请求头,可以一并配置进来
  11. }
  12. },
  13. })

跨域、超时、响应码处理

axios 中,提供是否允许跨域的属性——withCredentials,以及配置超时时间的属性——timeout,通过这两个属性,可以轻松处理跨域和超时的问题。

下面,我们来说说响应码处理:

axios 提供了 validateStatus 属性,用于定义对于给定的HTTP 响应状态码是 resolve 或 reject promise。所以,正常设置的情况下,我们会将状态码为 2 系列或者 304 的请求设为 resolve 状态,其余为 reject 状态。结果就是,我们可以在业务代码里,使用 catch 统一捕获响应错误的请求,从而进行统一处理。

但是,由于我在代码里面使用了 async-await,而众所周知,async-await 捕获 catch 的方式极为麻烦,所以,在此处,我选择将所有响应都设为 resolve 状态,统一在 then 处理。

此部分代码如下:

  1. const service = axios.create({
  2. // 跨域请求时是否需要使用凭证
  3. withCredentials: true,
  4. // 请求 30s 超时
  5. timeout: 30000,
  6. validateStatus: function () {
  7. // 使用async-await,处理reject情况较为繁琐,所以全部返回resolve,在业务代码中处理异常
  8. return true
  9. },
  10. })

请求、响应处理

在不使用 axios 的情况下,每次请求或者接受响应,都需要将请求或者响应序列化。

而在 axios 中, transformRequest 允许在向服务器发送请求前,修改请求数据;transformResponse 在传递给 then/catch 前,允许修改响应数据。

通过这两个钩子,可以省去大量重复的序列化代码。

代码如下:

  1. const service = axios.create({
  2. // 在向服务器发送请求前,序列化请求数据
  3. transformRequest: [function (data) {
  4. data = JSON.stringify(data)
  5. return data
  6. }],
  7. // 在传递给 then/catch 前,修改响应数据
  8. transformResponse: [function (data) {
  9. if (typeof data === 'string' && data.startsWith('{')) {
  10. data = JSON.parse(data)
  11. }
  12. return data
  13. }]
  14. })

拦截器

拦截器,分为请求拦截器以及响应拦截器,分别在请求或响应被 then 或 catch 处理前拦截它们。

之前提到过,由于 async-await 中 catch 难以处理的问题,所以将出错的情况也作为 resolve 状态进行处理。但这带来了一个问题,请求或响应出错的情况下,结果没有数据协议中定义的 msg 字段(消息)。所以,我们需要在出错的时候,手动生成一个符合返回格式的返回数据。

由于,在业务中,没有需要在请求拦截器中做额外处理的需求,所以,请求拦截器的 resolve 状态,只需直接返回就可以了。

请求拦截器代码如下:

  1. // 请求拦截器
  2. service.interceptors.request.use((config) => {
  3. return config
  4. }, (error) => {
  5. // 错误抛到业务代码
  6. error.data = {}
  7. error.data.msg = '服务器异常,请联系管理员!'
  8. return Promise.resolve(error)
  9. })

再来聊聊响应拦截器,还是之前的那个问题,除了请求或响应错误,还有一种情况也会导致返回的消息体不符合协议规范,那就是状态码不为 2 系列或 304 时。此时,我们还是需要做一样的处理——手动生成一个符合返回格式的返回数据。但是,有一点不一样,我们还需要根据不同的状态码生成不同的提示信息,以方便处理上线后的问题。

响应拦截器代码如下:

  1. // 根据不同的状态码,生成不同的提示信息
  2. const showStatus = (status) => {
  3. let message = ''
  4. // 这一坨代码可以使用策略模式进行优化
  5. switch (status) {
  6. case 400:
  7. message = '请求错误(400)'
  8. break
  9. case 401:
  10. message = '未授权,请重新登录(401)'
  11. break
  12. case 403:
  13. message = '拒绝访问(403)'
  14. break
  15. case 404:
  16. message = '请求出错(404)'
  17. break
  18. case 408:
  19. message = '请求超时(408)'
  20. break
  21. case 500:
  22. message = '服务器错误(500)'
  23. break
  24. case 501:
  25. message = '服务未实现(501)'
  26. break
  27. case 502:
  28. message = '网络错误(502)'
  29. break
  30. case 503:
  31. message = '服务不可用(503)'
  32. break
  33. case 504:
  34. message = '网络超时(504)'
  35. break
  36. case 505:
  37. message = 'HTTP版本不受支持(505)'
  38. break
  39. default:
  40. message = `连接出错(${status})!`
  41. }
  42. return `${message},请检查网络或联系管理员!`
  43. }
  44.  
  45. // 响应拦截器
  46. service.interceptors.response.use((response) => {
  47. const status = response.status
  48. let msg = ''
  49. if (status < 200 || status >= 300) {
  50. // 处理http错误,抛到业务代码
  51. msg = showStatus(status)
  52. if (typeof response.data === 'string') {
  53. response.data = { msg }
  54. } else {
  55. response.data.msg = msg
  56. }
  57. }
  58. return response
  59. }, (error) => {
  60. // 错误抛到业务代码
  61. error.data = {}
  62. error.data.msg = '请求超时或服务器异常,请检查网络或联系管理员!'
  63. return Promise.resolve(error)
  64. })

tips:友情提示,上面那一坨 switch-case 代码,可以使用策略模式进行优化~

支持 TypeScript

由于前段时间,我在部门内推了 TypeScript,为了满足自己的强迫症,将所有 js 文件改写为了 ts 文件。由于 axios 本身有 TypeScript 相关的支持,所以只需要把对应的类型导入,然后赋值即可。

完整代码

  1. // http.ts
  2. import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
  3.  
  4. const showStatus = (status: number) => {
  5. let message = ''
  6. switch (status) {
  7. case 400:
  8. message = '请求错误(400)'
  9. break
  10. case 401:
  11. message = '未授权,请重新登录(401)'
  12. break
  13. case 403:
  14. message = '拒绝访问(403)'
  15. break
  16. case 404:
  17. message = '请求出错(404)'
  18. break
  19. case 408:
  20. message = '请求超时(408)'
  21. break
  22. case 500:
  23. message = '服务器错误(500)'
  24. break
  25. case 501:
  26. message = '服务未实现(501)'
  27. break
  28. case 502:
  29. message = '网络错误(502)'
  30. break
  31. case 503:
  32. message = '服务不可用(503)'
  33. break
  34. case 504:
  35. message = '网络超时(504)'
  36. break
  37. case 505:
  38. message = 'HTTP版本不受支持(505)'
  39. break
  40. default:
  41. message = `连接出错(${status})!`
  42. }
  43. return `${message},请检查网络或联系管理员!`
  44. }
  45.  
  46. const service = axios.create({
  47. // 联调
  48. baseURL: process.env.NODE_ENV === 'production' ? `/` : '/apis',
  49. headers: {
  50. get: {
  51. 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  52. },
  53. post: {
  54. 'Content-Type': 'application/json;charset=utf-8'
  55. }
  56. },
  57. // 是否跨站点访问控制请求
  58. withCredentials: true,
  59. timeout: 30000,
  60. transformRequest: [(data) => {
  61. data = JSON.stringify(data)
  62. return data
  63. }],
  64. validateStatus () {
  65. // 使用async-await,处理reject情况较为繁琐,所以全部返回resolve,在业务代码中处理异常
  66. return true
  67. },
  68. transformResponse: [(data) => {
  69. if (typeof data === 'string' && data.startsWith('{')) {
  70. data = JSON.parse(data)
  71. }
  72. return data
  73. }]
  74. })
  75.  
  76. // 请求拦截器
  77. service.interceptors.request.use((config: AxiosRequestConfig) => {
  78. return config
  79. }, (error) => {
  80. // 错误抛到业务代码
  81. error.data = {}
  82. error.data.msg = '服务器异常,请联系管理员!'
  83. return Promise.resolve(error)
  84. })
  85.  
  86. // 响应拦截器
  87. service.interceptors.response.use((response: AxiosResponse) => {
  88. const status = response.status
  89. let msg = ''
  90. if (status < 200 || status >= 300) {
  91. // 处理http错误,抛到业务代码
  92. msg = showStatus(status)
  93. if (typeof response.data === 'string') {
  94. response.data = {msg}
  95. } else {
  96. response.data.msg = msg
  97. }
  98. }
  99. return response
  100. }, (error) => {
  101. // 错误抛到业务代码
  102. error.data = {}
  103. error.data.msg = '请求超时或服务器异常,请检查网络或联系管理员!'
  104. return Promise.resolve(error)
  105. })
  106.  
  107. export default service

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对w3xue的支持。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号