zoukankan      html  css  js  c++  java
  • 把axios封装为vue插件使用

    前言

    自从Vue2.0推荐大家使用 axios 开始,axios 被越来越多的人所了解。使用axios发起一个请求对大家来说是比较简单的事情,但是axios没有进行封装复用,项目越来越大,引起的代码冗余。就会非常麻烦的一件事。所以本文会详细的跟大家介绍,如何封装请求,并且在项目组件中复用请求。有需要的朋友可以做一下参考。

    封装的基本要求

    • 统一 url 配置
    • 统一 api 请求
    • request (请求)拦截器,例如:带上token等,设置请求头
    • response (响应)拦截器,例如:统一错误处理,页面重定向等
    • 根据需要,结合 Vuex 做全局的loading动画,或者错误处理
    • 将 axios 封装成 Vue 插件使用

    文件结构

    在src目录下新建 http 文件夹

    config.js axios的默认配置
    api.js 二次封装axios,拦截器等
    interface.js 请求接口文件
    index.js 将axios封装成插件

    config.js

    完整配置请参考 axios 的官方文档

    export default {
      method: 'get',
      // 基础url前缀
      baseURL: 'https://www.example.com/api',
      // 请求头信息
      headers: {
        'Content-Type': 'application/json;charset=UTF-8'
      },
      // 参数
      data: {},
      // 设置超时时间
      timeout: 10000,
      // 携带凭证
      withCredentials: true,
      // 返回数据类型
      responseType: 'json'
    }
    
    

    api.js

    import axios from 'axios';
    import config from './config';
    import qs from 'qs';
    import Cookies from "js-cookie";
    import router from '@/router'
    
    // 使用vuex做全局loading时使用
    // import store from '@/store'
    
    export default function $axios(options) {
      return new Promise((resolve, reject) => {
        const instance = axios.create({
          baseURL: config.baseURL,
          headers: {},
          transformResponse: [function (data) {
          }]
        })
    
        // request 拦截器
        instance.interceptors.request.use(
          config => {
            let token = Cookies.get('markToken')
            // 1. 请求开始的时候可以结合 vuex 开启全屏 loading 动画
            // console.log(store.state.loading)
            // console.log('准备发送请求...')
            // 2. 带上token
            if (token) {
              config.headers.accessToken = token
            } else {
              // 重定向到登录页面
              router.push('/login')
            }
            // 3. 根据请求方法,序列化传来的参数,根据后端需求是否序列化
            if (config.method === 'post') {
              if (config.data.__proto__ === FormData.prototype
                || config.url.endsWith('path')
                || config.url.endsWith('mark')
                || config.url.endsWith('patchs')
              ) {
              } else {
                config.data = qs.stringify(config.data)
              }
            }
            return config
          },
    
          error => {
            // 请求错误时
            console.log('request:', error)
            // 1. 判断请求超时
            if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
              console.log('timeout请求超时')
              // return service.request(originalRequest);//再重复请求一次
            }
            // 2. 需要重定向到错误页面
            const errorInfo = error.response
            console.log(errorInfo)
            if (errorInfo) {
              //error =errorInfo.data  //页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
              const errorStatus = errorInfo.status; // 404 403 500 ...
              router.push({
                path: `/error/${errorStatus}`
              })
            }
            return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
          }
        )
    
        // response 拦截器
        instance.interceptors.response.use(
          response => {
            let data;
            // IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
            if (response.data == undefined) {
              data = JSON.parse(response.request.responseText)
            } else {
              data = response.data
            }
    
            // 根据返回的code值来做不同的处理
            switch (data.rc) {
              case 1:
                console.log(data.desc)
                break;
              case 0:
                store.commit('changeState')
                // console.log('登录成功')
              default:
            }
            // 若不是正确的返回code,且已经登录,就抛出错误
            // const err = new Error(data.desc)
            // err.data = data
            // err.response = response
            // throw err
    
    
            return data
          },
          err => {
            if (err && err.response) {
              switch (err.response.status) {
                case 400:
                  err.message = '请求错误'
                  break
    
                case 401:
                  err.message = '未授权,请登录'
                  break
    
                case 403:
                  err.message = '拒绝访问'
                  break
    
                case 404:
                  err.message = `请求地址出错: ${err.response.config.url}`
                  break
    
                case 408:
                  err.message = '请求超时'
                  break
    
                case 500:
                  err.message = '服务器内部错误'
                  break
    
                case 501:
                  err.message = '服务未实现'
                  break
    
                case 502:
                  err.message = '网关错误'
                  break
    
                case 503:
                  err.message = '服务不可用'
                  break
    
                case 504:
                  err.message = '网关超时'
                  break
    
                case 505:
                  err.message = 'HTTP版本不受支持'
                  break
    
                default:
              }
            }
            console.error(err)
            return Promise.reject(err) // 返回接口返回的错误信息
          }
        )
    
        // 请求处理
        instance(options).then(res => {
          resolve(res)
          return false
        }).catch(error => {
          reject(error)
        })
      })
    }
    
    

    interface.js

    import axios from './api'
    
    /* 将所有接口统一起来便于维护
     * 如果项目很大可以将 url 独立成文件,接口分成不同的模块
     */
    
    // 单独导出
    export const query = () => {
        return axios({
            url: '/query',
            method: 'get'
        })
    }
      
    export const list = (id) => {
        return axios({
            url: `/list${id}`,
            method: 'get'
        })
    }
    
    export const upload = data => {
        return axios({
            url: '/upload',
            method: 'post',
            data
        })
    }
    
    // 默认全部导出
    export default {
        query,
        list,
        upload
    }
    

    index.js

    封装成 Vue 插件

    // 导入所有接口
    import apiList from './interface'
    const install = Vue => {
        if (install.installed) 
            return;
        install.installed = true;
    
        Object.defineProperties(Vue.prototype, {
            // 注意哦,此处挂载在 Vue 原型的 $api 对象上
            $api: {
                get() {
                    return apiList
                }
            }
        })
    }
    
    export default install
    
    

    使用

    到此为止,万事俱备就差用了,在 mian.js 中做如下操作:

    import api from './http/index'
    Vue.use(api)
    // 此时可以直接在 Vue 原型上调用 $api 了
    
    

    在 vue 中使用

    // List.vue
    
    ...
    this.$api.list(id).then(res => {
         if (res.rc === 0) {
              this.pageList = res.data.item
          } else {
            this.$Message.info(res.desc);
          }
         })
         .catch(error => {
            this.$Message.info(error);
          })
    ...
    
    

    总结

    • 以上二次封装较为全面,基本完成了我们之前的需求
    • 在错误的处理上还需要与后端协定好返回值,做具体的约定

    本文同步发布在 https://www.cssge.com


    参考文章

  • 相关阅读:
    mybatis中refid是什么意思
    spring源码之—Assert.notNull()
    MySQL数据库查询 concat 字段合并 身份证 名字手机号脱敏 case when等
    Java 8 引入的一个很有趣的特性是 Optional 类
    <if test="distinct">distinct </if> 这样写的问题
    bit类型数据,是1=false 还是0=false
    开发用的软件
    写代码的心得
    SQL Server数据库级别触发器
    python访问aws-S3服务
  • 原文地址:https://www.cnblogs.com/zhouyangla/p/9121779.html
Copyright © 2011-2022 走看看