1. 程式人生 > >解析Vue二次封裝axios為外掛使用

解析Vue二次封裝axios為外掛使用

vuejs2.0 已經不再維護 vue-resource,vuejs2.0 已經使用了 axios,這也是為什麼我會轉到 axios 的主要原因,廢話不多說:
基本的封裝要求:

  • 統一 url 配置
  • 統一 api 請求
  • request (請求)攔截器,例如:帶上token等,設定請求頭
  • response (響應)攔截器,例如:統一錯誤處理,頁面重定向等
  • 根據需要,結合 Vuex 做全域性的loading動畫,或者錯誤處理
  • 將 axios 封裝成 Vue 外掛使用

檔案結構
使用 vue-cli 進行相關的封裝,在 src 資料夾下:

src 
| 
-- http 封裝axios模組資料夾
| 
---- config.js axios的預設配置
---- api.js 二次封裝axios,攔截器等 
---- interface.js 請求介面檔案 
---- index.js 將axios封裝成外掛

config.js
預設配置參照 gitHub,以下只做示例:

export default {
  method: 'post',
  // 基礎url字首
  baseURL: 'http://qm.qq.com/cgi-bin/qm/qr?k=7aFQitBKLfNUDg3JDbAll697s4MzVV4n',
  // 請求頭資訊
  headers: {
   'Content-Type':'application/json;charset=UTF-8'
  },
  // 引數
  data: {},
  // 設定超時時間
  timeout: 10000,
  // 攜帶憑證
  withCredentials: false,
  // 返回資料型別
  responseType: 'json'
}

api.js

import axios from 'axios' // 注意先安裝哦
import config from './config.js' // 倒入預設配置
import qs from 'qs' // 序列化請求資料,視服務端的要求
 
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 => {
      // Tip: 1
      // 請求開始的時候可以結合 vuex 開啟全屏的 loading 動畫
 
      // Tip: 2 
      // 帶上 token , 可以結合 vuex 或者重 localStorage
      // if (store.getters.token) {
      //   config.headers['X-Token'] = getToken() // 讓每個請求攜帶token--['X-Token']為自定義key 請根據實際情況自行修改
      // } else {
      //   // 重定向到登入頁面  
      // }
 
      // Tip: 3
      // 根據請求方法,序列化傳來的引數,根據後端需求是否序列化
      if (config.method.toLocaleLowerCase() === 'post'
        || config.method.toLocaleLowerCase() === 'put'
        || config.method.toLocaleLowerCase() === 'delete') {
 
        config.data = qs.stringify(config.data)
      }
      return config
    },
    error => {
      // 請求錯誤時做些事(介面錯誤、超時等)
      // Tip: 4
      // 關閉loadding
      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 = response.request.responseText
      } else{
        data = response.data
      }
      // 根據返回的code值來做不同的處理(和後端約定)
      switch (data.code) {
        case '':
        break;
        default:
      }
      // 若不是正確的返回code,且已經登入,就丟擲錯誤
      // const err = new Error(data.description)
 
      // 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)
      // 此處我使用的是 element UI 的提示元件
      // Message.error(`ERROR: ${err}`);
      return Promise.reject(err) // 返回介面返回的錯誤資訊
    }
  )
  
  //請求處理
  instance(options)
    .then((res) => {
      resolve(res)
      return false
    })
    .catch((error) => {
      reject(error)
    })
  })
}

interface.js

import axios from './api' // 倒入 api
 
/* 將所有介面統一起來便於維護
 * 如果專案很大可以將 url 獨立成檔案,介面分成不同的模組
 * 此處的資料依然來自 Easy Mock
 */
 
// 單獨倒出
export const query = params => {
  return axios({
    url: '/query',
    method: 'get',
    params
  })
}
export const mock = params => {
  return axios({
    url: '/mock',
    method: 'get',
    params
  })
} 
export const upload = data => {
  return axios({
    url: '/upload',
    method: 'post',
    data
  })
// 預設全部倒出
// 根絕需要進行 
export default {
  query,
  mock,
  upload
}

index.js
封裝成 Vue 外掛,便(提)於(高)使(B)用(格)

// 倒入所有介面
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 中做如下操作:

// 倒入 http 資料夾下的 index.js
import api from './http/index'
Vue.use(api)
?
// 此時可以直接在 Vue 原型上呼叫 $api 了

總結

  • 以上二次封裝較為全面,基本完成了我們之前的需求
  • 在錯誤的處理上還需要與後端協定好返回值,做具體的約定
  • 封裝回調有點多,在使用的時候也需要加上 then() 來處理結果

PS: IE9 不支援 Promise 哦,需要安裝一個 polyfill
import 'babel-polyfill'

為了學習工作與休閒娛樂互不衝突,現新建圈【碼農茶水鋪】用於程式設計師生活,愛好,交友,求職招聘,吐槽等話題交流,希望各位大神工作之餘到茶水鋪來喝茶聊天。群號:582735936