axios二次封裝學習

xyz木楠發表於2019-03-04

封裝的必要性

我們在使用axios進行非同步操作時,可能會遇到以下情況:

  • 對一個按鈕頻繁點選,傳送多次請求
  • axios的規範寫法中:
    axios.post(url, data).then(res=>{}).catch(err=>{})
    複製程式碼

    這裡我們發現我們每一次寫的時候,都需要寫.catch(err=>{}),會造成程式碼的冗餘

封裝過程

攔截器科普

// 新增請求攔截器
axios.interceptors.request.use(function (config) {
    // 在傳送請求之前做些什麼
    return config;
}, function (error) {
    // 對請求錯誤做些什麼
    return Promise.reject(error);
});
// 新增響應攔截器
axios.interceptors.response.use(function (response) {
    // 對響應資料做點什麼
    return response;
}, function (error) {
    // 對響應錯誤做點什麼
    return Promise.reject(error);
});
複製程式碼

取消請求處理方法科普

var CancelToken = axios.CancelToken;
var cancel;

axios.get(`/user/12345`, {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函式接收一個 cancel 函式作為引數
    cancel = c;
  })
});

// 取消請求
cancel();
複製程式碼

本文程式碼

axios.js
設定一個cancelFlag作為標誌符,預設為true,在請求攔截器時,判斷如果cancelFlag為true,就可以傳送請求,且將cancelFlag設為false。當cancelFlag為false,就取消請求。在響應攔截器中再將cancelFlag設為true。說明只用當一個請求傳送且收到響應後,才可以傳送另一個請求。這裡存在的問題:cancelFlag是全域性變數,這樣多頁面多個介面請求時,互相會有影響這裡的解決辦法就是在axios.js中構建建構函式,這樣可以讓cancelFlag私有化,但是這樣的方式會導致佔有大量記憶體。
參考同事寫的程式碼(版本二),我覺得非常的有道理。而且比較簡單。
版本一、使用cancelFlag全域性變數判斷

import Vue from `vue`
import axios from `axios`
import {Indicator} from `mint-ui`
Vue.component(Indicator)
let CancelToken = axios.CancelToken //取消請求
let cancelFlag = true
//設定公共部分,請求頭和超時時間
axios.defaults.headers = {
    `X-Requested-With`: `XMLHttpRequest`
}
axios.defaults.timeout = 20000
//在請求攔截器時
axios.interceptors.request.use(config => {
    if (cancelFlag) {
        cancelFlag = false
        Indicator.open()
    } else {
        cancelToken: new CancelToken (c => {
            cancel = c
        })
        cancel()
    }
    return config
}, error => {
    return Promise.reject(error)
})
axios.interceptors.response.use(config => {
    cancelFlag = true
    Indicator.close()
    return config
}, error => {
    //
})
複製程式碼

版本二、非同步請求時,帶上一個引數requestName。
這裡一開始的疑惑是,當請求a帶上引數requestName後,傳送多次請求,判斷axios[requestName]和axios[requestName].cancel存在時,會做取消處理。那傳送成功後,再點選時,axios[requestName]和axios[requestName].cancel還是會存在啊。這樣還是會執行axios[requestName].cancel()。
這裡是因為當上一次請求傳送成功後,其axios[requestName].cancel這個方法已經失效,即使執行了這個方法也不起作用。axios[requestName].cancel的值永遠是上一次的請求的取消回撥。當上一次請求成功後,該回撥會失效。

axios.interceptors.request.use(config => {
    let requestName = config.data.requestName
    if (requestName) {
        if (axios[requestName] && axios[requestName].cancel) {
            axios[requestName].cancel()
        }
        config.cancelToken = new CancelToken (c => {
            axios[requestName] = {}
            axios[requestName].cancel = c
        })
    }
    return config
}, error => {
    return Promise.reject(error)
})
複製程式碼

響應的錯誤處理封裝

axios.interceptors.response.use(config => {
    Indicator.close()
    return config
}, error => {
    cancelFlag = true
    Indicator.close()
    if (error && error.response) {
        switch (error.response.status) {
            case 400:
                error.message = `錯誤請求`
                break;
            case 401:
                error.message = `未授權,請重新登入`
                break;
            case 403:
                error.message = `拒絕訪問`
                break;
            case 404:
                error.message = `請求錯誤,未找到該資源`
                break;
            case 405:
                error.message = `請求方法未允許`
                break;
            case 408:
                error.message = `請求超時`
                break;
            case 500:
                error.message = `伺服器端出錯`
                break;
            case 501:
                error.message = `網路未實現`
                break;
            case 502:
                error.message = `網路錯誤`
                break;
            case 503:
                error.message = `服務不可用`
                break;
            case 504:
                error.message = `網路超時`
                break;
            case 505:
                error.message = `http版本不支援該請求`
                break;
            default:
            error.message = `連線錯誤${error.response.status}`
        }
      } else {
        error.message = "連線到伺服器失敗"
      }
    return Promise.reject(error.message)
})
複製程式碼

http.js(封裝了post和get請求)
在axios.js檔案裡對響應攔截器做了判斷error.response.status的值的處理,根據不同的狀態碼返回不同的error說明。在http.js檔案裡post和get函式的引數為三個,第三個引數error就是出現錯誤時的文案。使用該api可以自己設定該文案,如果不傳這個引數,那麼就返回axios.js設定的error文案

import Vue from `vue`
import axios from `./axios`
import `mint-ui/lib/style.css`;
import {Toast} from `mint-ui`
Vue.component(Toast)
export function post (url, data, error) {
    return new Promise((resolve, reject) => {
        axios.post(url, data).then(res => {
            resolve(res)
        }, err => {
            err = error ? error : err
            Toast({
                message: err,
                duration: 500
            })
        })
    })
}
export function get (url, data, error) {
    return new Promise((resolve, reject) => {
        axios.post(url, {
            data: data
        }).then(res => {
            resolve(res)
        }, err => {
            err = error ? error : err
            Toast({
                message: err,
                duration: 500
            })
        })
    })
}
複製程式碼

使用

1)在main.js引入檔案

import axios from `../utils/axios.js`
import {post, get} from `../utils/http.js`
Vue.prototype.$axios = axios
Vue.prototype.$post = post
Vue.prototype.$get = get
複製程式碼

2)元件中使用

this.$post(`/api/saveInfo`, {
    value: this.value,
    requestName: `name01`
}, `請求失敗啦~~~`).then(res => {
    // alert(res.data)
})
複製程式碼

程式碼:github地址連結

相關文章