一直想封裝一下 axios, 能夠方便項目中使用,今天有時間,就好好研究了一下。vue
// util/axios.js import axios from 'axios' const pending = {} const CancelToken = axios.CancelToken const removePending = (key, isRequest = false) => { if (pending[key] && isRequest) { pending[key]('取消重複請求') } delete pending[key] } const getRequestIdentify = (config, isReuest = false) => { let url = config.url if (isReuest) { url = config.baseURL + config.url.substring(1, config.url.length) } return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data)) } // 請求攔截器 axios.interceptors.request.use(config => { // 攔截重複請求(即當前正在進行的相同請求) let requestData = getRequestIdentify(config, true) removePending(requestData, true) config.cancelToken = new CancelToken((c) => { pending[requestData] = c }) return config }, error => { return Promise.reject(error) }) // 異常處理 axios.interceptors.response.use(response => { // 把已經完成的請求從 pending 中移除 let requestData = getRequestIdentify(response.config) removePending(requestData) return { code: response.status, message: response.statusText, data: response.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 = '請求錯誤,未找到該資源' break case 405: err.message = '請求方法未容許' 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: err.message = `鏈接錯誤${err.response.status}` } let errData = { code: err.response.status, message: err.message } // 統一錯誤處理能夠放這,例如頁面提示錯誤... console.log('統一錯誤處理: ', errData) } return Promise.reject(err) }) axios.defaults.baseURL = 'http://localhost:3000/' export default (instance) => { instance.prototype.axios = (data) => { var _params = { method: !data.method ? 'get' : data.method.toLowerCase(), url: data.url } if (_params.method === 'get') { _params.params = data.params || {} } else { _params.data = data.params || {} } return new Promise((resolve, reject) => { axios(_params).then(response => { resolve(response) }).catch(error => { reject(error) }) }) } }
// main.js import axios from './util/axios' Vue.use(axios)
// HelloWorld.vue methods: { getUserInfo (_id) { this.axios({ url: '/users', method: 'get', params: { 'id': _id } }).then(response => { console.log(response) }) } }
本人使用 json-server 搭建 mock 服務(這個,有必要的話,以後會寫一下),服務器地址爲http://localhost:3000/
,因此設置 axios 的 基礎URL路徑設置爲http://localhost:3000/
。ios
另外,你們有須要的話,也能夠對 axios.defaults.headers
默認的請求頭、axios.defaults.timeout
請求超時時間 進行設置。這裏就不設置了。git
axios.defaults.baseURL = 'http://localhost:3000/'
這裏,get、post 請求具體調用的時候,都經過 this.axios(requestData)
來調用,其中 requestData
有統一的格式,以下github
const requestData = { url: '/users', // 必填 method: 'get', // 選填,默認 'get' params: {} // 選填,默認 {} }
這部分經過 requestData.method
處理 axios發送請求時,requestData.params
是賦值給 _params.params
(get 請求傳遞參數) 仍是賦值給 _params.data
(post 請求傳遞參數)。json
export default (instance) => { instance.prototype.axios = (data) => { var _params = { method: !data.method ? 'get' : data.method.toLowerCase(), url: data.url } if (_params.method === 'get') { _params.params = data.params || {} } else { _params.data = data.params || {} } return new Promise((resolve, reject) => { axios(_params).then(response => { resolve(response) }).catch(error => { reject(error) }) }) } }
下面函數,經過一個請求參數中的 url
, params
(get 請求傳遞參數)或 data
(post 請求傳遞參數)來表示每個請求。axios
使用請求路徑加請求參數的標識方式,避免了相同請求路徑,不一樣請求參數的狀況下的錯誤攔截。segmentfault
其中須要注意的地方是,請求攔截器中 config.url = '/users'
, 響應攔截器中 config.url = 'http://localhost:3000/users'
,因此加上一個標識isReuest
來計算請求的全路徑服務器
/** * config: 請求數據 * isReuest: 請求攔截器中 config.url = '/users', 響應攔截器中 config.url = 'http://localhost:3000/users',因此加上一個標識來計算請求的全路徑 */ const getRequestIdentify = (config, isReuest = false) => { let url = config.url if (isReuest) { url = config.baseURL + config.url.substring(1, config.url.length) } return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data)) }
使用 cancel token
取消請求。網絡
這裏每一個請求經過傳遞一個 executor
函數到 CancelToken
的構造函數來建立 cancel token
。函數
// 添加請求攔截器 axios.interceptors.request.use(config => { // 發送請求以前,攔截重複請求(即當前正在進行的相同請求) let requestData = getRequestIdentify(config, true) removePending(requestData, true) config.cancelToken = new CancelToken((c) => { pending[requestData] = c }) return config }, error => { return Promise.reject(error) })
這一步是結合上面的請求攔截器執行的,取消重複請求的同時刪除記錄,而且在下面的響應攔截器也會調用該函數,即完成請求後刪除請求記錄。
// key: 請求標識;isRequest 完成請求後也須要執行刪除記錄,因此添加此參數避免執行無用操做 const removePending = (key, isRequest = false) => { if (pending[key] && isRequest) { pending[key]('取消重複請求') } delete pending[key] // 把這條記錄從 pending 中移除 }
可使用響應攔截器來統一處理請求異常,例如,統一返回的數據的格式、統一處理異常報錯...
// 異常處理 axios.interceptors.response.use(response => { // 把已經完成的請求從 pending 中移除 let requestData = getRequestIdentify(response.config) removePending(requestData) return { code: response.status, message: response.statusText, data: response.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 = '請求錯誤,未找到該資源' break case 405: err.message = '請求方法未容許' 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: err.message = `鏈接錯誤${err.response.status}` } let errData = { code: err.response.status, message: err.message } // 統一錯誤處理能夠放這,例如頁面提示錯誤... console.log('統一錯誤處理: ', errData) } return Promise.reject(err) })
CancelToken 這一部分,還不是很清楚原理,但願你們指導一下~~~
axios 的 github 地址
https://segmentfault.com/a/11...
https://www.jianshu.com/p/444...