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);
          })
    ...

    总结

    • 以上二次封装较为全面,基本完成了我们之前的需求
    • 在错误的处理上还需要与后端协定好返回值,做具体的约定
    • 转自:http://www.cnblogs.com/zhouyangla/p/9121779.html
  • 相关阅读:
    Service Name Port Number Transport Protocol tcp udp 端口号16bit
    linux linux 互传文件 win 不通过 ftp sftp 往linux 传文件(文件夹)
    soft deletion Google SRE 保障数据完整性的手段
    Taylor series
    Taylor's theorem
    Moving average
    REQUEST
    Unix file types
    mysqld.sock
    Tunneling protocol
  • 原文地址:https://www.cnblogs.com/xuqp/p/9213061.html
Copyright © 2011-2022 走看看