axios 二次封裝 api的統籌管理 配合async await實際專案中的運用

weixin_34234823發表於2018-10-15

寫在前面


作者簡書地址
axios在實戰專案中的運用,所舉例專案是基於vue全家桶(vue-router+vuex+axios+element-ui)的後臺管理系統,需要一些有vue專案開發經驗的讀者閱讀。
由於vue-resource 作者宣佈不再更新,促使我們使用第三方的request資料請求,一般我們的選擇有

  1. JQuery ajax
  • 本身是針對MVC的程式設計,不符合現在前端MVVM的浪潮
  • 基於原生的XHR開發,XHR本身的架構不清晰,已經有了fetch的替代方案
  • JQuery整個專案太大,單純使用ajax卻要引入整個JQuery非常的不合理(採取個性化打包的方案又不能享受CDN服務)
  1. fetch
  • 符合關注分離,沒有將輸入、輸出和用事件來跟蹤的狀態混雜在一個物件裡
    更好更方便的寫法
  • 更加底層,提供的API豐富(request, response)
  • 脫離了XHR,是ES規範裡新的實現方式
    1)fetchtch只對網路請求報錯,對400,500都當做成功的請求,需要封裝去處理
    2)fetch預設不會帶cookie,需要新增配置項
    3)fetch不支援abort,不支援超時控制,使用setTimeout及Promise.reject的實現的超時控制並不能阻止請求過程繼續在後臺執行,造成了量的浪費
    4)fetch沒有辦法原生監測請求的進度,而XHR可以
  1. axios
  • 從瀏覽器中建立 XMLHttpRequest
  • 從 node.js 發出 http 請求
  • 支援 Promise API
  • 攔截請求和響應
  • 轉換請求和響應資料
  • 取消請求
  • 自動轉換JSON資料
  • 客戶端支援防止CSRF/XSRF

題外話(一)

一般在專案中我們會常常遇到跨域的問題,主要受同源策略(協議,路徑,埠)的影響
尤其是使用vue-cli這種腳手架工具開發時,由於專案本身啟動本地服務是需要佔用一個埠的,所以必然會產生跨域的問題。
在vue-cli專案中webpack的proxyTable幫我們很好的處理了跨域問題:


7878840-64e013c65a01d720.png
image.png

如何配置:在專案生成的config目錄下的index.js找
有些專案可能要接不同域名下的介面,而proxyTable支援配置多個域名,有這種需求的小夥伴可以自行配置。

劃下重點------>
axios的請求會在你的url路徑上自動拼接你的target(前提是我這種管理api的方式,後面會提)
記住我畫的那個部分(記住這個配置)

 proxyTable: {
      '/api/**': {
        target: 'https://www.jianshu.com:8080/', //==>你的目標域名和埠
        changeOrigin: true,
        pathRewrite: {
          '^/': '/'
        }
      },
    }

主題開始

參考axios-中文使用手冊:https://www.kancloud.cn/yunye/axios/234845

一、在我們接Api的時候 主要有兩個階段

  • request 請求階段 ===》對應了axios.interceptors.request 請求攔截器過濾請求
  • response 響應階段 ===》對應了axios.interceptors.response 響應攔截器過濾響應
    我一般在專案中建一個API資料夾 統一管理api 以及 axios的request的封裝方法 目錄為下


    7878840-af30157fe214d3fb.png
    image.png

下面直接上程式碼了:

import axios from 'axios'
import { throwErr } from '@/utils' //utils 捕捉服務端http狀態碼的方法
import store from '@/store'   //引入vuex的相關操作
import { Message } from 'element-ui' //element Toast的提示
import router from '@/router'

//過濾請求
axios.interceptors.request.use(config => {
  //config 為請求的一些配置 例如:請求頭 請求時間 Token  可以根據自己的專案需求個性化配置,參考axios的中文說明手冊  自己多動動手
 //由於我們專案的後端大大給力,很多東西在服務端幫我們處理好了所以請求階段只要傳好引數就好了
  config.timeout = 10 * 1000 //請求響應時間
  return config
}, error => {
  return Promise.reject(error)
})
// 新增響應攔截器
axios.interceptors.response.use(
  response => {
    if (response.data.code === 0) {   //服務端定義的響應code碼為0時請求成功
      return Promise.resolve(response.data) //使用Promise.resolve 正常響應
    } else if (response.data.code === 1401) { //服務端定義的響應code碼為1401時為未登入
      store.dispatch('setUserInfo', {})
      Message({
        message: '未登入'
      })
      // router.push('/login')
      return Promise.reject(response.data)    //使用Promise.reject 丟擲錯誤和異常
    } else {
      return Promise.reject(response.data)
    }
  },
  error => {
    if (error && error.response) {
      let res = {}
      res.code = error.response.status
      res.msg = throwErr(error.response.status, error.response) //throwErr 捕捉服務端的http狀態碼 定義在utils工具類的方法
      return Promise.reject(res)
    }
    return Promise.reject(error)
  }
)
export default function request(method, url, data) {  //暴露 request 給我們好API 管理
  method = method.toLocaleLowerCase()   //封裝RESTful API的各種請求方式 以 post get delete為例
  if (method === 'post') {
    return axios.post(url, data)    //axios的post 預設轉化為json格式
  } else if (method === 'get') {     
    return axios.get(url, {
      params: data
    })
  } else if (method === 'delete') {
    return axios.delete(url, {
      params: data
    })
  }
}

以下為throwErr的原始碼

//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
}

如果有的小夥伴 是'application/x-www-form-urlencoded'為請求格式的話
可以使用 npm i qs -S 使用qs包將引數序列化

  transformRequest:[function(data){
      //在這裡根據自己的需求改變資料
      return qs.stringify(data,{arrayFormat:'repeat'})
    }],

二 在api管理資料夾中如何定義介面呢?

以account.js為例

  • 引入request.js
  • 暴露你定義的api介面
  • request 的三個引數(mtehod,url,params)
    1. method 及axios的請求方法 (post,get,delete ···Ï)
    2. url 服務端的介面路徑
      3.params 介面所傳遞的引數 全部以物件的形式傳入 (後面會提 不著急)
7878840-95fda7d3f61a13d1.png
image.png
import request from './request' //引入axios的封裝方法

export const getAdminList = (params) => {
 return request('get', '/api/v1.0/admin/list', params) //登陸管理員獲取自身資訊
}

export const register = (params) => {
 return request('post', '/api/v1.0/admin/register', params) //新增管理員
}

export const deleteAdmin = (id, params) => {
 return request('delete', `/api/v1.0/admin/${id}`, params) //更新管理員資訊
}

是不是很簡單 管理的介面就是這麼簡潔帥氣 為我打個call

在專案中如何使用定義的介面?

1.在頁面中import對應的模組api檔案以及其中的介面
2.不管是哪種請求 我們全部用物件(鍵值對)的形式傳引數
3.用async 和await 在methods中定義介面方法

  • 用來區分我們所定義的普通方法
  • 可以方便我們處理函式回撥 用同步的思想寫程式碼 方便理解
  • 不用.then那種鏈式寫法 美化程式碼 增加可讀性和理解性
  • 配合try catch用來捕錯
7878840-f7f84ecd74286cb7.png
image.png

此例子可以在我的上篇關於 vue mixin的文章結合觀看 觀碼效果更加
傳送門

 getParams() {
      return {
        role: this.role,
        name: this.name,
        status: this.status,
        start: (this.PAGINATION.currentPage - 1) * this.PAGINATION.pageSize,
        range: this.PAGINATION.pageSize,
      }
    },
    async getAdminList() {
      this.loading = true
      let res = await getAdminList(this.queryParams)
      this.tableData = res.data.list
      this.PAGINATION.total = res.data.count
      this.loading = false
    },
  async deleteAdmin() {
      try {
        let res = await deleteAdmin(this.id)
        this.$message({
          type: 'success',
          message: '刪除成功'
        })
        this.getAdminList()
      } catch (err) {
        this.$message({
          type: 'error',
          message: err.msg
        })
      }
    },
 addParams() {
      let { role, username, name, password, gender, profession, education, signature } = this.form
      return { role, username, name, password, gender, profession, education, signature }
    },
 async register() {
      try {
        let res = await register({ ...this.addParams() })
        this.$message({
          type: 'success',
          message: '新增成功'
        })
        this.dialogFormVisible = false
        this.$emit('listen')
      } catch (err) {
        this.$message({
          type: 'error',
          message: err.msg
        })
      }
    },

在實際表現如下
1.get 操作表現


7878840-aa9cb438e06b646e.png
image.png

2.delete操作表現


7878840-7e6c3ba60a36b8d3.png
image.png

3.post 操作


7878840-3d163aa41cf38fe8.png
image.png

題外話(二)

關於RESTful API的理解 (主要是後端開發者觀看,前端小夥伴瞭解即可)

精簡點:

  • http的動詞(get post delete put patch ···)描述操作
  • url地址定位資源

RESTful API:

這是一種設計風格而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端與伺服器互動類的軟體。基於這個風格設計的軟體可以更簡潔,更有層級,更易於實現快取等機制。在這種風格中,每個url路徑代表一種資源(resource),所以路徑中不推薦有名詞,而且所用的名詞往往與資料庫的表格名對應,且一般採取複數的形式命名。而對應資源的具體操作型別,而由HTTP動詞表示,即 GET/POST/PUT/PATCH/DELETE

7878840-7da2a015d5729af6.png
image.png

這是我在看書過程中所閱讀到的,具體書名就不透露了,免得有打廣告嫌疑

寫在後面

以上就是axios的二次封裝以及如何在專案中如何優雅的使用,統一使用物件傳參的方式。我覺得這種方式很方便優雅。因此將它分享出來,希望大家會喜歡。覺得有用的小夥伴可以給個心,給個關注,有不懂的地方可以評論我和私信我,有空會一一解答。下次會分享一篇在vue中如何將tinyMce封裝成元件,tinyMCe富文字編輯器如何上傳圖片和檔案。
發現簡書的百度seo不怎麼好而且不防爬蟲,第二天搜尋自己的標題發現文章在其它網站上而且是第一條,根本搜不到自己的
簡書首發網址

相關文章