axios的二次封裝與async,await的配合使用?

weixin_34377065發表於2018-11-25

前言:前些日子讀文章,說是vue3.0會在明年下半年正式推出,改動的地方好像也不少,比如說vue3.x的程式碼庫將會用typescript編寫,並提供改進的 TypeScript 支援.變化還是很快的,雖然水平還差的遠,可路還是一步一步走吧.ok,回到正題,在vue2.x的版本中請求資料推薦使用的axios,之前我常常使用的axios的別名方法,比如說在vue元件中:

axios.get('http://localhost:3000/user')
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log(err)
    })
複製程式碼

如果一個頁面發多個請求,那寫法上還是比較麻煩的,並沒有async,await的寫法清爽,也沒有體現出所謂物件導向程式設計啊,es6中類的思想和優勢.所以文章分三部分,一部分是axios的二次封裝,另一部分是async,await的使用,再一部分是對別人的借鑑!

axios的二次封裝

以vue2.x的webpack為例說明

src資料夾結構目錄

基礎原理

使用axios去自定義建立例項,掛載請求所需配置項

axios.create([config])

//抄來的案例
const instance = axios.create({
    baseURL: 'http://exemple.com/api/',
    timeout: 1000,
    header: {'X-Custom-Header': 'foobar'}
})
複製程式碼

axios的github地址

檔案說明

config資料夾下的url.js檔案

// axios請求的預設請求地址,區分生產環境與開發環境
// 題外話,vue2.x專案中會有伺服器代理的配置項(上篇文章有提及)
// 此處的的axios的預設地址會拼接在代理配置項的基地址之後
// 所以呢,結合實際情況設定axios的預設請求地址
export const baseURL = process.env.NODE_ENV === 'production'
  ? '/production'
  : '/development'

複製程式碼

libs資料夾下的axios.js檔案

import axios from 'axios'
import {baseURL} from '@/config/url'
// 主要是在處理put,post等請求的傳參情況
// 有時後端會需要將傳遞的物件引數轉化成urlcode的形式
// qs就能發揮相應的作用
import qs from 'qs'

// es6當中類的一個寫法,物件導向思想的一個體現
// 不明白自行查資料哦
class HttpRequest {
  constructor (baseUrl = baseURL) {
    this.baseUrl = baseUrl
  }
  
// 設定axios的請求例項的預設配置項
  getInsideConfig () {
    const config = {
      baseUrl: this.baseUrl,
      headers: {
        //
      }
    }
    return config
  }

// axios的攔截器
  interceptors (instance) {
  
    // 請求攔截器(比如在此處驗證一下是否有發請求的許可權)
    instance.interceptors.request.use(config => {
      return config
    }, error => {
      return Promise.reject(error)
    })
    
    // 響應攔截器
    // (比如對返回的引數做一些處理,比如請求狀態的提示語的處理)
    // (拿data中的資料,response物件可能是有很多層data的)
    instance.interceptors.response.use(res => {
      return res
    }, err => {
      return Promise.reject(err)
    })
  }

// 此處即為建立請求例項,真正在請求中發揮作用的地方
  create (options) {
    if (options.method === 'post' || options.method === 'put') {
      options.headers['Content-Type'] = 'application/x-www-form-urlencoded'
      
      // post,put請求需要將傳遞的資料掛載在data項上
      // 後臺需要傳遞引數urlcode化/引數序列化時,使用qs
      options.data = qs.stringify(options.data)
    } else {
    
      // get等請求的資料掛在params項上,當然params項上也可以掛post等請求的引數,
      // 但是不嚴謹,且引數拼接到位址列上也不安全,有長度限制
      options.params = options.data
    }
    return axios.create(options)
  }

// 暴露到其他檔案中被使用的方法,options引數也是外部檔案傳遞進來的
  request (options) {
  
  // Object.assign返回一個將引數物件的屬性合併後的一個物件
    options = Object.assign(this.getInsideConfig(), options)

    const instance = this.create(options)

    this.interceptors(instance)

    return instance(options)
  }
}

export default HttpRequest

複製程式碼

libs資料夾下axiosRequest.js檔案

// 實際上本檔案只是起了一箇中轉作用,一個類的實現或者叫例項化
import HttpRequest from '@/libs/axios'

const axios = new HttpRequest()

export default axios

複製程式碼

api資料夾下order.js檔案

// system.js是系統設定模組的一些請求方法
// 訂單模組,請求方法的集中處理
import axios from '@/libs/axiosRequest'

// 獲取訂單列表(重新整理頁面時,進入的是未處理訂單)
export const getOrderList = (getData) => {
  const data = {
    page: getData.page,
    rows: getData.rows,
    orderStatus: getData.orderStatus,
    keyWord: getData.keyWord
  }
  return axios.request({
    url: '/carscraporder-manager/order',
    data,
    method: 'get'
  })
}
// 獲取訂單詳情
export const getOrderDetail = (id) => {
  return axios.request({
    url: `/carscraporder-manager/order/${id}`,
    method: 'get'
  })
}
// 舊件訂單派單異常
export const abnormalPartOrder = (id, data) => {
  return axios.request({
    url: `/carscraporder-manager/order/audit/${id}`,
    data,
    method: 'put'
  })
}
複製程式碼

async,await的使用

views資料夾下Order.vue檔案

// 按需引入請求方法
<script>
import { getOrderList, getHistoryOrderList, getAllOrderList, getOrderDetail } from '@/api/order'
export default {
    data() {
        return {
          // 判定當前所要查詢的訂單狀態,預設1為未處理,2為已處理,3為全部
          orderState: 1,
          // 要傳遞給舊件編輯頁面的表單資料
          partOrderDetail: {},
          // 要傳遞給整車編輯頁面的表單資料
          newOrderdetail: {},
          tableData: [],
          query: '',
          pagenum: 1,
          total: 0,
          // 舊件編輯頁面開關變數
          partEditvisible: false,
          // 整車編輯頁面開關變數
          allEditvisible: false,
          options: [],
          currentRoleId: -1
        }
    },
    methods: {
    // 全部訂單
    async allOrderList () {
      this.pagenum = 1
      this.orderState = 3
      let data = {
        page: 1,
        rows: 10
      }
      const response = await getAllOrderList(data)
      if (response.data.code === 200) {
        this.tableData = response.data.data.rows
        this.total = response.data.data.total
      } else {
        this.$message.error('全部訂單列表獲取失敗')
      }
    },
    }
}
</script>
複製程式碼

別人的一些觀點和優化

  • 響應攔截時錯誤http狀態碼的處理

引用詳情

//axios捕錯,需要引入的一個檔案
export const throwErr = (code, response) => {
  let message = '請求錯誤'
  switch (code) {
    case 400:
      message = '請求錯誤'
      break
    case 401:
      message = '未授權,請登入'
      break
    case 403:
      message = '拒絕訪問'
      break
    case 404:
      message = `請求地址出錯: ${response.config.url}`
      break
    case 408:
      message = '請求超時'
      break
    case 500:
      message = '伺服器內部錯誤'
      break
    case 501:
      message = '服務未實現'
      break
    case 502:
      message = '閘道器錯誤'
      break
    case 503:
      message = '服務不可用'
      break
    case 504:
      message = '閘道器超時'
      break
    case 505:
      message = 'HTTP版本不受支援'
      break
    default:
  }
  return message
}

複製程式碼
  • 響應之後銷燬請求例項
class httpRequest {
  constructor () {
    // 儲存請求佇列
    this.queue = {};
  }
  
 // 銷燬請求例項
  destroy (url) {
    delete this.queue[url];
    
    // Object.keys:屬性名字串組成的一個陣列
    const queue = Object.keys(this.queue);
    return queue.length;
  }  
  
  interceptors (instance, url) {
    instance.interceptors.response.use((res) => {
      let { data } = res;
      this.destroy(url);
    }
  }
  // 請求例項
  request (options) {
    var instance = this.create(options);
    // url為屬性名,instance為屬性值
    this.queue[options.url] = instance;
  }
複製程式碼

相關文章