其實axios已經提供了不少很強大的api,咱們在實際使用中直接調用就能夠,可是每一個團隊每一個項目調用axios的姿式不同,特別是在一個大型的團隊項目中,與後端交互邏輯迥異、配置複雜、地址繁多,因此一個風格統一化、配置靈活化、管理集中化的請求方案必不可少。javascript
首先在項目中建立一個名爲api
的文件夾,用來統一管理與後臺交互的邏輯。java
在api
文件夾下分別建立文件夾,按類型對接口地址進行分類(這點頗有必要,特別是在大型項目中,按類型進行分類能讓你快速定位到接口寫入的位置):ios
index.js
文件,寫入屬於這個類型的全部接口地址:
cmsgit
...,
export const CMS_DATA = '/cms/renderData'
複製代碼
membergithub
...,
export const MEMBER_INFO = '/rights/memberInfo'
複製代碼
在api
文件夾下建立index.js
文件:json
把全部類型的接口統一暴露出去:axios
// cms信息
export * from './cms'
// 會員信息
export * from './member'
複製代碼
在api
文件夾下建立cache.js
文件:後端
基於axios
開發了一套緩存機制,基於請求地址url
和請求參數params
給每一個請求結果進行緩存,同時能夠給每一個請求結果設置緩存有限期和緩存模式:api
export default class Cache {
constructor(axios, config = {}) {
this.axios = axios
this.caches = []
if (!this.axios) {
throw new Error('請傳入axios實例')
}
this.config = config
this.defaultConfig = {
cache: false,
expire: 100 * 1000
}
this.CancelToken = this.axios.CancelToken
this.init()
}
init() {
this.requestInterceptor(this.config.requestInterceptorFn)
this.responseInterceptor(this.config.responseInterceptorFn)
window.onbeforeunload = () => {
this.mapStorage()
}
}
requestInterceptor(callback) {
this.axios.interceptors.request.use(async config => {
let newConfig = callback && (await callback(config))
config = newConfig || config
let { url, data, params, cacheMode, cache = this.defaultConfig.cache, expire = this.defaultConfig.expire } = config
if (cache === true) {
let getKey = data ? `${url}?cacheParams=${data}` : `${url}?cacheParams=${params}`
let obj = this.getStorage(cacheMode, getKey)
// 判斷緩存數據是否存在
if (obj) {
let curTime = this.getExpireTime()
let source = this.CancelToken.source()
config.cancelToken = source.token
// 判斷緩存數據是否存在,存在的話是否過時,若是沒過時就中止請求返回緩存
if (curTime - obj.expire < expire) {
source.cancel(obj)
} else {
this.removeStorage(cacheMode, url)
}
}
} else {
this.clearStorage(url)
}
return config
}, error => {
return Promise.reject(error)
})
}
responseInterceptor(callback) {
this.axios.interceptors.response.use(async response => {
let newResponse = callback && (await callback(response))
response = newResponse || response
// the http request error, do not store the result, direct return result
if (response.status !== 200 || response.data.ret || !response.data.success) {
return response.data
}
/* * `data` is the data to be sent as the request body, only applicable for request methods 'PUT', 'POST', and 'PATCH' * `params` are the URL parameters to be sent with the request, can be applicable for request methods 'GET' */
let { url, cache, cacheMode, data, params } = response.config
if (cache === true) {
let obj = {
expire: this.getExpireTime(),
params,
data,
result: response.data
}
let setKey = data ? `${url}?cacheParams=${data}` : `${url}?cacheParams=${params}`
this.caches.push(setKey)
this.setStorage(cacheMode, setKey, obj)
}
return response.data
}, error => {
// 返回緩存數據
if (this.axios.isCancel(error)) {
return Promise.resolve(error.message.result)
}
return Promise.reject(error)
})
}
// 設置緩存
setStorage(mode = 'sessionStorage', key, cache) {
window[mode].setItem(key, JSON.stringify(cache))
}
// 獲取緩存
getStorage(mode = 'sessionStorage', key) {
let data = window[mode].getItem(key)
return JSON.parse(data)
}
// 清除緩存
removeStorage(mode = 'sessionStorage', key) {
window[mode].removeItem(key)
}
// 設置過時時間
getExpireTime() {
return new Date().getTime()
}
// 清空緩存
clearStorage(key) {
if (window.localStorage.getItem(key)) {
window.localStorage.removeItem(key)
} else {
window.sessionStorage.removeItem(key)
}
}
// 清空沒用到的緩存
mapStorage() {
let length = window.localStorage.length
if (length) {
for (let i = 0; i < length; i++) {
let key = window.localStorage.key(i)
if (!this.caches.includes(key) && key.includes('?cacheParams=')) {
window.localStorage.removeItem(key)
}
}
}
}
}
複製代碼
因爲緩存機制是基於
url+params
來進行緩存的,在有效期內再次訪問相同的url+params
,瀏覽器就會直接讀取緩存不會再發送請求。若是有效期過了或者請求地址變了或者請求參數變了,瀏覽器則會繞過緩存直接發送請求。(支持分頁緩存的場景)瀏覽器
在api
文件夾下建立config.js
文件,用來存放axios
的一些預配置信息:
import axios from 'axios'
import Cache from './cache'
axios.defaults.withCredentials = true
axios.defaults.baseURL = process.env.NODE_ENV === 'production' ? '' : '/api'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
...
複製代碼
因爲設計的緩存機制依賴攔截器機制,爲了不額外的攔截器配置,在設計緩存機制時留有攔截器配置入口,以下:
new Cache(axios, {
requestInterceptorFn: config => {
// 自定義請求攔截器
/* */
// 須要用Promise將config返回
return Promise.resolve(config)
},
responseInterceptorFn: response => {
// 自定義響應攔截器,可統一返回的數據格式也可攔截錯誤
/* */
// 須要用Promise將response返回
return Promise.resolve(response)
}
})
export default axios
複製代碼
若是沒有采用緩存機制的話,能夠直接配置攔截器,以下:
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
export default axios
複製代碼
在api
文件夾下建立base.js
文件:
主要封裝幾個經常使用的方法,這裏就列舉經常使用的get
和post
方法:
import axios from './config'
import qs from 'qs'
export const post = (url, data, extend = {isJson: true, cache: false}) => {
let defaultConfig = {
url,
method: 'POST',
data: extend.isJson ? data : qs.stringify(data) // 經過isJson來肯定傳參格式是json仍是formData,默認是json
}
let config = {...defaultConfig, ...extend}
return axios(config).then(res => {
// 能夠統一返回的數據格式
return Promise.resolve(res)
}, err => {
return Promise.reject(err)
})
}
export const get = (url, data, extend = {cache: false}) => {
let defaultConfig = {
url,
method: 'GET',
params: data
}
let config = {...defaultConfig, ...extend}
return axios(config).then(res => {
// 能夠統一返回的數據格式
return Promise.resolve(res)
}, err => {
return Promise.reject(err)
})
}
複製代碼
在api
文件夾下建立install.js
文件:
把封裝好的方法註冊到全局:
import { get, post } from 'api/base'
export const install = function(Vue, config = {}) {
Vue.prototype.$_get = get
Vue.prototype.$_post = post
}
複製代碼
在main.js
中寫入:
import { install as Axios } from './api/install'
Vue.use(Axios)
複製代碼
調用時只須要引入想要調用的接口地址就行:
import { CMS_DATA, MEMBER_INFO } from 'api'
methods: {
receiveCMS() {
// post參數形式爲formData
this.$_post(CMS_DATA, data, { jsJson: false }).then(res => {
console.log(res)
}),
},
receiveMember() {
// 開啓緩存,設置緩存時間爲一個小時,緩存的模式爲localStorage
this.$_get(MEMBER_INFO, data, { cache: true, expires: 1000 * 60 * 60, cacheMode: 'localStorage' }).then(res => {
console.log(res)
}),
}
}
複製代碼
緩存默認是關閉的,須要手動開啓,若是開啓的話,緩存有效期默認是10分鐘,緩存的模式默認爲
sessionStorage
。
整個設計好的方案就完成了:
這個方案說不上最好,但目前是我總結出來最優雅的方式了,也歡迎大佬們提出寶貴的意見。