封裝的必要性
我們在使用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地址連結