前言
在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。如果还对axios不了解的,可以移步axios文档。
网上也很多对axios进行二次封装的,但我觉得大多都不够通用或者写得不够全面,所以在借鉴大家的封装经验,我进行补充,形成我觉得完善的封装,当然还可以在此之上不断扩展,没有最好只有更好。
二次封装的目的
-
便于维护:对于一个项目来说,代码一定要利于维护,当细节发生变化时,可修改接口内部实现,接口使用方无需配套修改。
-
统一实现:封装底层实现,形成项目特有的api实现,可统一和规范项目中的代码实现,避免天花乱坠的实现,从而难以维护。
-
程序复用:封装时,需充分考虑程序的可复用性,通过提供接口api的形式让使用者来实现细节。
二次封装的效果
-
将axios封装成Vue插件使用
-
统一api请求(基于Vue全局、基于单个Vue实例)
-
统一axios默认值配置方式
-
暴露接口统一由使用方配置请求、响应拦截器
-
暴露接口统一由使用方配置请求、响应拦截器
-
内部默认实现重复请求拦截的拦截器
封装实现
import axios from 'axios'
import qs from 'qs'
import $ from 'jquery'
axios.defaults.timeout = 10000
axios.defaults.baseURL = '' // 填写域名,需以/结尾,后续在拼装URL时使用
axios.defaults.withCredentials = false // 表示跨域请求时是否需要使用凭证
// ------------------------------私有内部方法-------------------------------
// axios 拦截重复请求
const pending = {}
const CancelToken = axios.CancelToken
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]('取消重复请求') // 执行CancelToken,用于取消进行中的请求
}
delete pending[key]
}
const getRequestIdentify = (config, isReuest = false) => {
// request的时候,config.url是action路径;response的时候,config.url是全路径。所以存在判断请求操作从而处理url
let url = config.url
if (isReuest) {
url = config.baseURL + config.url.substring(1, config.url.length)
}
return config.method === 'get' || config.method === 'delete' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(url + JSON.stringify(config.data))
}
/**
* 封装get post delete put方法
* @param method
* @param url
* @param data
* @param selfConfig 单个请求的个性化配置
* @returns {Promise}
*/
function methodAxios (method, url, params, selfConfig = {}) {
let httpDefault = {
method: method,
url: url,
// `params` 是即将与请求一起发送的 URL 参数
// `data` 是作为请求主体被发送的数据
params: method === 'GET' || method === 'DELETE' ? params : null,
data: method === 'POST' || method === 'PUT' ? qs.stringify(params) : null
}
let requestConfig = $.extend({}, httpDefault, selfConfig)
// 这个其实可以直接return axios(requestConfig),为何需要再增加一层Promise?
// 这里是有原因的:
// 1、如果直接return axios(requestConfig),请求成功或失败的处理是交由使用者
// 2、这里封装多一层Promise,是便于此处封装时考虑添加公共处理如开启遮罩层关闭遮罩层,之后才抛出调用结果给调用方,而不应该由调用方赖关闭遮罩层
return new Promise((resolve, reject) => {
axios(requestConfig)
.then((response) => {
resolve(response)
}).catch((error) => {
reject(error)
})
})
}
/**
* 封装添加axios All
* @param promiseArray
* @returns {Promise}
*/
function allAxios (promiseArray) {
// 这个其实可以直接axios.all(promiseArray),为何需要再增加一层Promise?
// 这里是有原因的:
// 1、如果直接axios.all(promiseArray),请求成功或失败的处理是交由使用者
// 2、这里封装多一层Promise,是便于此处封装时考虑添加公共处理如开启遮罩层关闭遮罩层,之后才抛出调用结果给调用方,而不应该由调用方赖关闭遮罩层
return new Promise((resolve, reject) => {
axios.all(promiseArray)
.then(allResponse => {
resolve(allResponse)
})
.catch((error) => {
reject(error)
})
})
}
/**
* 封装添加请求拦截器方法
* @param interceptorFun
* @param errorFun
* @returns {Interceptor}
*/
function setRequestInterceptor (interceptorFun, errorFun) {
return axios.interceptors.request.use(interceptorFun, errorFun)
}
/**
* 封装卸载请求拦截器方法
* @param interceptor
* @returns {Interceptor}
*/
function ejectRequestInterceptor (interceptor) {
return axios.interceptors.request.eject(interceptor)
}
/**
* 封装添加响应拦截器方法
* @param interceptorFun
* @param errorFun
* @returns {Interceptor}
*/
function setResponseInterceptor (interceptorFun, errorFun) {
return axios.interceptors.response.use(interceptorFun, errorFun)
}
/**
* 封装卸载响应拦截器方法
* @param interceptor
* @returns {Interceptor}
*/
function ejectResponseInterceptor (interceptor) {
return axios.interceptors.response.eject(interceptor)
}
/**
* 封装设置axios默认参数值方法
* @param key 需要是在默认值列表中key
* @param value
*/
function setDefaultValue (key, value) {
axios.defaults[key] = value
}
/**
* 封装设置axios默认参数值方法
* @param key 需要是在默认值列表中key
*/
function setDefaultValues (defaultValues) {
for (let key in defaultValues) {
axios.defaults[key] = defaultValues[key]
}
}
// -------------------------------基础公共配置----------------------
// 请求拦截器--公共
// 1、headers添加Content-Type
// 2、拦截重复请求
setRequestInterceptor(
config => {
config.headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
}
// 拦截重复请求(即当前正在进行的相同请求)
let requestData = getRequestIdentify(config, true)
removePending(requestData, true)
config.cancelToken = new CancelToken((c) => {
pending[requestData] = c
})
return config
},
error => {
// Do something with request error
return Promise.reject(error)
}
)
// 响应拦截器--公共
// 1、拦截重复请求
// 2、对响应结果进行判断--请求状态
setResponseInterceptor(
response => {
// 把已经完成的请求从 pending 中移除
let requestData = getRequestIdentify(response.config)
removePending(requestData)
return response
}, error => {
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 {
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) { // 请求超时
console.log('连接请求超时')
}
}
return Promise.reject(error)
}
)
// 输出函数getAxios、postAxios、putAxios、delectAxios,供其他文件调用-----------------------------
// Vue.js的插件应当有一个公开方法 install。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
// 封装给vue的this.$faceAxios和Vue.faceAxios使用的公共对象方法集合
const faceAxiosObject = {
get: (url, params, selfConfig) => methodAxios('GET', url, params, selfConfig),
post: (url, params, selfConfig) => methodAxios('POST', url, params, selfConfig),
put: (url, params, selfConfig) => methodAxios('PUT', url, params, selfConfig),
delete: (url, params, selfConfig) => methodAxios('DELETE', url, params, selfConfig),
all: (promiseArr) => allAxios(promiseArr)
}
// 用于Vue安装的对象
const install = Vue => {
if (install.installed) {
return
}
install.installed = true
// 基于全局Vue对象使用faceAxios
Vue.faceAxios = faceAxiosObject
// 在一个Vue实例内使用$faceAxios
Object.defineProperties(Vue.prototype, {
$faceAxios: {
get () {
return faceAxiosObject
}
}
})
}
export default {
install,
// 基于全局的公共方法
setRequestInterceptor: (interceptorFun, errorFun) => setRequestInterceptor(interceptorFun, errorFun),
ejectRequestInterceptor: (interceptor) => ejectRequestInterceptor(interceptor),
setResponseInterceptor: (interceptorFun, errorFun) => setResponseInterceptor(interceptorFun, errorFun),
ejectResponseInterceptor: (interceptor) => ejectResponseInterceptor(interceptor),
setDefaultValue: (key, value) => setDefaultValue(key, value),
setDefaultValues: (defaultValues) => setDefaultValues(defaultValues)
}
封装使用
import Vue from 'vue'
import FaceAxios from '@/faceAxios/api' // 我是存放在src的faceAxios目录
Vue.use(FaceAxios)
//全局使用
FaceAxios.setDefaultValue('baseURL', 'http://localhost:8080/') // 设置公共参数
FaceAxios.setDefaultValues({
'baseURL': 'http://localhost:8080/'
})
FaceAxios.setRequestInterceptor() // 设置拦截器
// 基于Vue的全局使用
Vue.faceAxios.post('/queryList', {id: '123'}, {timeout: 5000}).then(...)
//基于单个Vue实例使用
this.$faceAxios.post('/queryList', {id: '123'}, {timeout: 5000}).then(...)
本人微信:iamhxb 有疑问或交流可添加好友联系
网友评论