最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 压箱底的 axios 封装类,了解一下?

    正文概述 掘金(紫夜流星)   2021-01-24   657

    下面开始我们的 axios 封装之旅。

    1、目录结构

    --api
      --request                    // 文件夹,存放封装类的处理逻辑相关
        --axiosApi.js              // 封装类导出文件
        --config.js                // 接口 baseURL 的配置文件,开发或者生产环境
        --HttpRequest.js           // 类逻辑处理封装文件
        --errorHandle.js           // 全局错误响应处理文件
      --urls                       // 文件夹,存放所有接口请求 URL 路径
        --users.js
        --log.js
        -- ...
      --index.js                   // 封装类引用入口文件
    

    2、引入所需模块

    HttpRequest.js 文件中,首先引入我们需要用到的模块,如下:

    import axios from 'axios' // 引入 axios 库
    import qs from 'qs' // qs 模块,用来系列化 post 类型的数据
    import store from '@/store' // 状态管理,用于设置 token 或者调用自定义的接口方法
    import errorHandle from './errorHandle' // 统一的错误响应处理
    import { getToken } from '@/utils/auth' // 从 localStorage 中获取 token
    

    3、HttpRequest 类封装

    1)定义默认配置

    默认配置中,我们可以根据是开发环境或者生产环境来定义对应的 baseURL,默认请求方式是 GETheader头信息设置接受的数据类型和请求发送的数据格式是 json,超时时间设定为10s,可根据不同接口请求的实际情况重新定义。

    class HttpRequest {
      // 设置默认值为空方便使用 devServer 代理
      constructor (baseURL = '') {
        this.defaultConfig = { // 默认配置
          baseURL,
          method: 'get',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json; charset=UTF-8'
          },
          timeout: 1000 * 10 // 请求超时时间
        },
        isErrorHandle: false // 是否开启全局错误响应提示,默认关闭
      }
    }
    

    2)创建 axios 实例

    创建 axios 实例时,会将用户传入的 options 配置与默认配置合并,然后调用定义好的 interceptors 拦截器(下面会讲到),最后将 axios 实例返回。

      /**
       * 创建 axios 实例
       *
       * @param {Object} options 用户自定义配置
       * @return {Axios} 返回 axios 实例
       * @memberof HttpRequest
       */
      createAxiosInstance (options) {
        const axiosInstance = axios.create()
        // 默认配置和用户自定义配置合并
        const newOptions = this.mergeOptions(this.defaultConfig, options)
        // 调用拦截器
        this.interceptors(axiosInstance)
        // 返回实例
        return axiosInstance(newOptions)
      }
    
      /**
       * 合并配置
       *
       * @param {Object} source 原配置项
       * @param {Object} target 目标配置项
       * @return {Object} 返回新配置
       * @memberof HttpRequest
       */
      mergeOptions (source, target) {
        if (typeof target !== 'object' || target == null) {
          return source
        }
        return Object.assign(source, target)
      }
    

    3)拦截器设置

    在拦截器中,我们将 axios 实例作为参数引入,然后定义 请求拦截器响应拦截器

    • 请求拦截器

      • 常规操作,每次请求都会带上 token 作为与后端数据交互的验证依据;
      • 这边只处理常用的 getpost 请求,根据配置中传进来的 method 属性来动态的执行请求方式(注意这里没有单独的封装 getpost 请求方法);
      • 传参方式有点区别, get 请求方式传参使用 params: {id: xx} 形式, post 方式则为 data: {id: xx} ;
      • 结合请求头的 Content-Type 属性的不同,来对传参对象序列化(详情看注释),这样后端才能正常接收到传的参数,当为上传文件类型时,需要自己在请求头中设置 'Content-Type': 'multipart/form-data;'
    • 响应拦截器

      • 拦截器处理比较简单,根据服务器响应的 status 状态码,正常响应时,将响应数据 data 返回出去,异常响应时,则将整个 response 以失败的形式返回出去。
    /**
     * 拦截器
     *
     * @param {Axios} instance
     * @memberof HttpRequest
     */
    interceptors (instance) {
      // 请求拦截器
      instance.interceptors.request.use((config) => {
        const { headers, method, params, data } = config
        // 每次请求都携带 token
        const token = getToken() || ''
        token && (headers.Authorization = token)
    
        // 如果 Content-type 类型不为 'multipart/form-data;' (文件上传类型 )
        if (!headers['Content-Type'].includes('multipart')) {
          // 如果请求方式为 post 方式,设置 Content-type 类型为 'application/x-www-form-urlencoded; charset=UTF-8'
          (method === 'post') && (headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8')
          // 根据 contentType 转换 data 数据
          const contentType = headers['Content-Type']
          // Content-type类型 'application/json;',服务器收到的raw body(原始数据) "{name:"nowThen",age:"18"}"(普通字符串)
          // Content-type类型 'application/x-www-form-urlencoded;',服务器收到的raw body(原始数据) name=nowThen&age=18
          const paramData = (method === 'get') ? params : data
          contentType && (config.data = contentType.includes('json') ? JSON.stringify(paramData) : qs.stringify(paramData))
        }
        return config
      }, (error) => {
        // 处理响应错误
        errorHandle(error)
        return Promise.reject(error)
      })
    
      // 响应拦截器
      instance.interceptors.response.use((response) => {
        const { status, data } = response
    
        // 正常响应
        if (status === 200 || (status < 300 || status === 304)) {
          if (data.code === 401) {
            // token 错误或者过期,需要重新登录,并清空 store 和 localstorge 中的 token
            store.dispatch('user/toLogin') // 跳转到登录界面
          }
          // 返回数据
          return Promise.resolve(data)
        }
        return Promise.reject(response)
      }, (error) => {
        // 处理响应错误
        errorHandle(error)
        return Promise.reject(error)
      })
    }
    
    • 拦截器异常处理
      • 如请求超时、网络异常 / 断开、未授权 / 拒绝访问等情况,则调用封装好的全局统一错误响应处理函数 errorHandle
      • 全局统一错误响应提示默认关闭,可通过配置 isErrorHandletrue 属性来开启。

    errorHandle在文件errorHandle.js 中定义,如下:

    import { message } from 'ant-design-vue'
    
    /**
    * axios统一错误处理主要针对HTTP状态码错误
    * @param {Object} err
    */
    function errorHandle (err) {
     // 判断服务器响应
     if (err.response) {
       switch (err.response.status) {
         // 用户无权限访问接口
         case 401:
           message.error('未授权,请先登录~')
           break
         case 403:
           message.error('服务器拒绝访问~')
           break
         case 404:
           message.error('请求的资源不存在~')
           break
         case 500:
           message.error('服务器异常,请稍后再试~')
           break
       }
     } else if (err.message.includes('timeout')) {
       message.error('连接超时~')
     } else if (
       err.code === 'ECONNABORTED' ||
       err.message === 'Network Error' ||
       !window.navigator.onLine
     ) {
       message.error('网络已断开,请检查连接~')
     } else {
       // 进行其他处理
       console.log(err.stack)
     }
    }
    
    export default errorHandle
    
    

    4)导出 HttpRequest 类

    在文件 HttpRequest.js 的末尾将 HttpRequest 类导出:

    export default HttpRequest
    

    4、HttpRequest 类使用

    1)api 引用入口文件

    index.js 入口文件中,首先要把封装好的实例引入进来,然后这里使用 webpack 给我们提供的 require.context API 来将定义好的 接口url 自动引入,这样我们就不用一个个手动导入了。 require.context API 使用可以参考我的另一篇文章:自动注册Vue组件【require.context】

    import api from './request/axiosApi' // 引入axios封装实例
    
    // https://webpack.js.org/guides/dependency-management/#requirecontext
    const apiFiles = require.context('./urls', true, /\.js$/)
    
    // 自动加载 urls 目录下的所有配置的接口
    const apiRequest = apiFiles.keys().reduce((apis, apiPath) => {
      const name = apiPath.replace(/^\.\/(.*)\.\w+$/, '$1')
      const value = apiFiles(apiPath)
    
      apis[name] = Object.keys(value.default).reduce((prev, cur) => {
        prev[cur] = (options = {}) => api.createAxiosInstance({ ...value.default[cur], ...options })
        return prev
      }, {})
    
      return apis
    }, {})
    
    // console.log(apiRequest)
    
    export default apiRequest
    

    当然,以上虽然实现了自动化引入,但是却引入了整个接口对象,而有时候我们需要 按需引入,则可以这样做:

    import api from './request/axiosApi' // 引入axios封装实例
    
    import users from './urls/users' // 引入 users api 接口配置
    
    const createApi = (apiUrls) => {
      return Object.keys(apiUrls).reduce((prev, cur) => {
        prev[cur] = (options = {}) => axios.createAxiosInstance({ ...apiUrls[cur], ...options })
        return prev
      }, {})
    }
    
    export const user = createApi(users)
    // 其他接口...
    

    2)接口 url 文件

    users.js 为例:

    export default {
      login: {
        method: 'post',
        url: 'user/login'
      },
      logout: {
        method: 'post',
        url: 'user/logout'
      },
      getInfo: {
        method: 'get',
        url: 'user/getInfo'
      },
      ...
    }
    
    

    3)axios 实例导出文件

    axiosApi.js

    import config from './config'
    import HttpRequest from './HttpRequest'
    
    // 根据当前环境获取API URL根路径
    const baseURL = process.env.NODE_ENV === 'production' ? config.baseURL.prod : config.baseURL.dev
    // 创建一个HtpRequest对象实例
    const axios = new HttpRequest(baseURL)
    
    export default axios
    

    4)api 使用

    • 全局引入

    在项目 main.js 入口文件中:

    import apiRequest from './api' // 引入封装的接口对象
    Vue.prototype.$api = apiRequest
    

    在组件中使用,接口请求示例:

    this.$api.user.getInfo({
    	params: {id: xx}
      })
      .then((res) => {
      	// 数据处理...
      })
      .catch((error) => {
      	// 异常处理...
      })
    
    • 按需引入

    在使用的地方,接口请求示例:

    import { user } from './api' // 按需引入封装的接口对象
    user.getInfo({
    	params: {id: xx}
      })
      .then((res) => {
      	// 数据处理...
      })
      .catch((error) => {
      	// 异常处理...
      })
    

    5、小结

    好了,把压箱底的 axios 封装拿出来稍微整理下,于是就产生了这篇文章,对于不同的项目业务需求,可能还有需要改进的地方,大家要是有更好的封装和想法,欢迎在下方导论提出优化建议,感谢你能耐心看到这里。


    起源地下载网 » 压箱底的 axios 封装类,了解一下?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元