1、安装
yarn add axios
2、 通用功能
- 正常请求该有的(跨域携带cookie,token,超时设置)
- 请求响应拦截器
- 取消重复请求
- 响应状态封装
- 解除重复请求锁定
- 取消请求
- 封装请求方法
3、功能实现
3.1 基础封装
axios设置拦截器:拦截每次请求和响应,简单使用方法如下:
- axios.interceptors.request.use(请求成功拦截, 请求失败拦截)
- axios.interceptors.response.use(响应成功拦截, 响应失败拦截)
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
// 创建axios实例
export const axiosInstance: AxiosInstance = axios.create({
baseURL: 'http://localhost:8080/', // 基础路径
timeout: 60 * 1000 //超时配置
})
// 请求拦截器
axiosInstance.interceptors.request
.use((config: AxiosRequestConfig) => {
// 在发送请求之前做些什么
return config
},(error:any) => {
// 对请求错误做些什么
return Promise.reject(error)
})
// 响应拦截器
axiosInstance.interceptors.response
.use((response: AxiosResponse) => {
// 对响应数据做点什么
return response
},(error:any) => {
// 对响应错误做点什么
return Promise.reject(error)
})
3.2 取消重复请求
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import {MessageType} from "element-ui/types/message";
import {Message} from "element-ui";
// 创建axios实例
export const axiosInstance: AxiosInstance = axios.create({
baseURL: 'http://localhost:8080/', // 基础路径
timeout: 60 * 1000 //超时配置
})
/**
* toast提示
* @param message
* @param type
*/
function dialog (type: MessageType, message = '') {
return Message({
message,
type,
duration: 5000,
showClose: true
})
}
// 记录正在请求的接口
// 每次请求前都会把path放在此数组中,响应成功后清除此请求path
let listenPendingAjax: any = {} // 对应字段为true说明已有该请求,还未结束
// 请求拦截器
axiosInstance.interceptors.request
.use((config: AxiosRequestConfig) => {
if (!config.url) {
return Promise.reject({
isCancel: true,
message: 'no url'
})
} else if (listenPendingAjax[config.url]) { // 拦截重复请求,为false时不做处理
return Promise.reject({
url: config.url,
isCancel: true,
message: '重复请求!'
})
} else if (!listenPendingAjax[config.url]) { // 正常请求
if (!config.cancelToken) {
const source = axios.CancelToken.source()
config.cancelToken = source.token
listenPendingAjax[config.url] = source
} else {
listenPendingAjax[config.url] = config.cancelToken
}
}
return config
},(error:any) => {
return Promise.reject(error)
})
// 响应拦截器
axiosInstance.interceptors.response
.use((response: AxiosResponse) => {
if (response.config.url) {
delete listenPendingAjax[response.config.url]
}
return response
},(error:any) => {
if (error.url && typeof error.url === 'string') {
delete listenPendingAjax[error.url]
} else { // 发生未知错误
listenPendingAjax = {}
}
return Promise.reject(error)
})
3.3 响应状态封装
// 响应拦截器
axiosInstance.interceptors.response
.use((response: AxiosResponse) => {
if (response.config.url) {
delete listenPendingAjax[response.config.url]
}
return response
},(error:any) => {
if (error.url && typeof error.url === 'string') {
delete listenPendingAjax[error.url]
} else { // 发生未知错误
listenPendingAjax = {}
}
axios.isCancel(error) && console.log('Request canceled: ' + error)
if (error.code === 'ECONNABORTED') {
error.message = '系统繁忙,请稍候再试'
}
if (/timeout/.test(error.message)) {
error.message = '系统超时,请稍候再试'
}
if (error.message === 'Network Error') {
error.message = '网络错误,请检查网络情况'
}
error.message && dialog('error', error.message)
return Promise.reject(error)
})
3.4 封装请求方法
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import {MessageType} from "element-ui/types/message";
import {Message} from "element-ui";
import qs from "qs";
// 创建axios实例
export const axiosInstance: AxiosInstance = axios.create({
baseURL: 'http://localhost:8080/', // 基础路径
timeout: 60 * 1000 //超时配置
})
/**
* toast提示
* @param message
* @param type
*/
function dialog (type: MessageType, message = '') {
return Message({
message,
type,
duration: 5000,
showClose: true
})
}
// 记录正在请求的接口
// 每次请求前都会把path放在此数组中,响应成功后清除此请求path
let listenPendingAjax: any = {} // 对应字段为true说明已有该请求,还未结束
// 请求拦截器
axiosInstance.interceptors.request
.use((config: AxiosRequestConfig) => {
if (!config.url) {
return Promise.reject({
isCancel: true,
message: 'no url'
})
} else if (listenPendingAjax[config.url]) { // 拦截重复请求,为false时不做处理
return Promise.reject({
url: config.url,
isCancel: true,
message: '重复请求!'
})
} else if (!listenPendingAjax[config.url]) { // 正常请求
if (!config.cancelToken) {
const source = axios.CancelToken.source()
config.cancelToken = source.token
listenPendingAjax[config.url] = source
} else {
listenPendingAjax[config.url] = config.cancelToken
}
}
return config
},(error:any) => {
return Promise.reject(error)
})
// 响应拦截器
axiosInstance.interceptors.response
.use((response: AxiosResponse) => {
if (response.config.url) {
delete listenPendingAjax[response.config.url]
}
return response
},(error:any) => {
if (error.url && typeof error.url === 'string') {
delete listenPendingAjax[error.url]
} else { // 发生未知错误
listenPendingAjax = {}
}
return Promise.reject(error)
})
/**
* 定义request类
* 封装请求方法
*/
export default class ServerRequest {
modulePath: string
nativeAxios: AxiosInstance
/**
* Api构造函数
* @param modulePath{string}
*/
constructor (modulePath: string) {
this.modulePath = modulePath
this.nativeAxios = axiosInstance
}
/**
* 获取调用api时整合出的url地址
* @param url{string}
* @returns {*}
*/
getModulePathUrl (url: string): string {
return /^https?:\/\//.test(url) ? url : this.modulePath + url
}
/**
* axios的request方法
* @param options{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
request (options: AxiosRequestConfig): Promise<AxiosResponse> {
options.url = this.getModulePathUrl(options.url || '')
return this.nativeAxios(options)
}
/**
* get请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
get (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.get(this.getModulePathUrl(url), config)
}
/**
* post请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
post (url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.post(this.getModulePathUrl(url), data, config)
}
/**
* put请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
put (url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.put(this.getModulePathUrl(url), data, config)
}
/**
* delete请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
delete (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.delete(this.getModulePathUrl(url), config)
}
/**
* head请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
head (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.delete(this.getModulePathUrl(url), config)
}
/**
* patch请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
patch (url: string, data: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.patch(this.getModulePathUrl(url), data, config)
}
}
3.5 解除重复请求锁定
/**
* 解除重复请求锁定,可以多次请求,解除可能会出现数据展示错乱的情况
* @param url{string}
* @returns {ServerRequest}
*/
unlock (url: string): ServerRequest {
listenPendingAjax[this.getModulePathUrl(url)] = false
return this
}
3.6 取消上一次请求,能保证数据是后面的请求返回的
/**
* 取消上一次请求,能保证数据是后面的请求返回的
* @param url
* @param params
*/
cancel (url: string, params?: any): ServerRequest {
if (params) {
const urlSearch = url.replace(/#.*$/, '').split('?')[1]
const urlHash = url.split('#')[1] ? '#' + url.split('#')[1] : ''
urlSearch && Object.assign(params, qs.parse(urlSearch))
url = url.split('?')[0] + '?' + qs.stringify(params) + urlHash
}
const resUrl = this.getModulePathUrl(url)
if (listenPendingAjax[resUrl]) {
listenPendingAjax[resUrl].cancel()
listenPendingAjax[resUrl] = undefined
}
return this
}
3.7 完整代码
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import {MessageType} from "element-ui/types/message"
import {Message} from "element-ui"
import qs from "qs"
// 创建axios实例
export const axiosInstance: AxiosInstance = axios.create({
baseURL: 'http://localhost:8080/', // 基础路径
timeout: 60 * 1000 //超时配置
})
/**
* toast提示
* @param message
* @param type
*/
function dialog (type: MessageType, message = '') {
return Message({
message,
type,
duration: 5000,
showClose: true
})
}
// 记录正在请求的接口
// 每次请求前都会把path放在此数组中,响应成功后清除此请求path
let listenPendingAjax: any = {} // 对应字段为true说明已有该请求,还未结束
// 请求拦截器
axiosInstance.interceptors.request
.use((config: AxiosRequestConfig) => {
if (!config.url) {
return Promise.reject({
isCancel: true,
message: 'no url'
})
} else if (listenPendingAjax[config.url]) { // 拦截重复请求,为false时不做处理
return Promise.reject({
url: config.url,
isCancel: true,
message: '重复请求!'
})
} else if (!listenPendingAjax[config.url]) { // 正常请求
if (!config.cancelToken) {
const source = axios.CancelToken.source()
config.cancelToken = source.token
listenPendingAjax[config.url] = source
} else {
listenPendingAjax[config.url] = config.cancelToken
}
}
return config
},(error:any) => {
return Promise.reject(error)
})
// 响应拦截器
axiosInstance.interceptors.response
.use((response: AxiosResponse) => {
if (response.config.url) {
delete listenPendingAjax[response.config.url]
}
return response
},(error:any) => {
if (error.url && typeof error.url === 'string') {
delete listenPendingAjax[error.url]
} else { // 发生未知错误
listenPendingAjax = {}
}
axios.isCancel(error) && console.log('Request canceled: ' + error)
if (error.code === 'ECONNABORTED') {
error.message = '系统繁忙,请稍候再试'
}
if (/timeout/.test(error.message)) {
error.message = '系统超时,请稍候再试'
}
if (error.message === 'Network Error') {
error.message = '网络错误,请检查网络情况'
}
error.message && dialog('error', error.message)
return Promise.reject(error)
})
/**
* 定义request类
* 封装请求方法
*/
export default class ServerRequest {
modulePath: string
nativeAxios: AxiosInstance
/**
* Api构造函数
* @param modulePath{string}
*/
constructor (modulePath: string) {
this.modulePath = modulePath
this.nativeAxios = axiosInstance
}
/**
* 获取调用api时整合出的url地址
* @param url{string}
* @returns {*}
*/
getModulePathUrl (url: string): string {
return /^https?:\/\//.test(url) ? url : this.modulePath + url
}
/**
* axios的request方法
* @param options{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
request (options: AxiosRequestConfig): Promise<AxiosResponse> {
options.url = this.getModulePathUrl(options.url || '')
return this.nativeAxios(options)
}
/**
* get请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
get (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.get(this.getModulePathUrl(url), config)
}
/**
* post请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
post (url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.post(this.getModulePathUrl(url), data, config)
}
/**
* put请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
put (url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.put(this.getModulePathUrl(url), data, config)
}
/**
* delete请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
delete (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.delete(this.getModulePathUrl(url), config)
}
/**
* head请求
* @param url{string}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
head (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.delete(this.getModulePathUrl(url), config)
}
/**
* patch请求
* @param url{string}
* @param data{any}
* @param config{AxiosRequestConfig}
* @returns {Promise<AxiosResponse>}
*/
patch (url: string, data: any, config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.nativeAxios.patch(this.getModulePathUrl(url), data, config)
}
/**
* 解除重复请求锁定,可以多次请求,解除可能会出现数据展示错乱的情况
* @param url{string}
* @returns {ServerRequest}
*/
unlock (url: string): ServerRequest {
listenPendingAjax[this.getModulePathUrl(url)] = false
return this
}
/**
* 取消上一次请求,能保证数据是后面的请求返回的
* @param url
* @param params
*/
cancel (url: string, params?: any): ServerRequest {
if (params) {
const urlSearch = url.replace(/#.*$/, '').split('?')[1]
const urlHash = url.split('#')[1] ? '#' + url.split('#')[1] : ''
urlSearch && Object.assign(params, qs.parse(urlSearch))
url = url.split('?')[0] + '?' + qs.stringify(params) + urlHash
}
const resUrl = this.getModulePathUrl(url)
if (listenPendingAjax[resUrl]) {
listenPendingAjax[resUrl].cancel()
listenPendingAjax[resUrl] = undefined
}
return this
}
}
3.8 使用
import ServerRequest from '@/utils/request.ts'
const api = new ServerRequest('/api')
export interface login {
userName: string,
passWord: string
}
export default {
/**
* 登录
*/
login: (data:login) => api.post('/login', data),
/**
* 测试
*/
test:(data:{id:number,name:string}) => api.get('',{params: data})
}