看了相关 axios相关源码 一起来看看Axios的源码 之后,我们可以开发一些 axios插件来帮助我们在业务开发中,更方便的使用 axios,提升开发效率。
业务需求
在业务开发中,对于异步请求的处理方式有多种多样,最常见的主要有以下几种:
- 请求时显示
loading - 请求和响应要处理
JWT header - 全局和局部的异常处理
- Tab切换时,取消上一次请求
- 第一次请求失败时,再重试几次
- 接口轮询
- 在一段时间里缓存幂等请求(get/head/option/put/delete)的请求结果
插件设计
- 插件支持全局默认配置
- 在注册插件时可以修改默认配置
- 在局部请求时也可以修改配置
- 局部请求可以开启相关功能
- 部分插件默认为关闭状态,需要在使用时局部开启,避免影响其它请求
示例1:通过设置为 true 来开启插件功能,作用于单个请求,默认都为 false
// 显示全局配置的 loading
api.login({ data, loading: true })
// 错误时重试
api.getUser({ data, retry: true })
// 出错时,使用全局配置的异常拦截器
api.getUser({ data, catchError: true })
// 重复请求时,取消上一次请求
api.getUser({ data, prevCancel: true })
示例2:通过配置对象来覆盖全局配置,同时开启插件功能
// 显示全局配置的 loading
api.login({ data, loading:{
handler: (isLoading, tip) => {
// 自定义处理loading
}
} })
// 错误时重试
api.getUser({ data, retry: {
// 失败时重试 5 次
count: 5
} })
// 出错时,使用全局配置的异常拦截器
api.getUser({ data, catchError: {
handler: (error) => {
// ... 自定义异常处理
}
} })
// 重复请求时,取消上一次请求
api.getUser({ data, prevCancel: {
// 判断相同请求时,也判断params
matchParams: true
} })
示例3: 没用使用 registApi 插件时,可以使用axios原生方式使用插件
// 显示全局配置的 loading
axios.post('/api/login', {}, { loading: true })
// 错误时重试
axios.get('/api/users', { retry: true })
registApi 注册API
注册API,简化调用方式
const apiConfig = {
getUser: 'get /user',
createUser: 'post /user',
deleteUser: id => `delete /user/${id}`
}
// api 注册
const api = axiosRegistApi(axios, {
apiConfig,
prefix: '/api'
});
const data = {a: 1, b: 2}
const id = 1;
api.getUser({ data }).then()
api.createUser({ data }).then()
api.deleteUser(id)({ data }).then().catch()
retry 重试
适用请求: get/head/options
请求失败时重试,当响应状态码为 500-599 时,重试当前请求
axiosRetry(axios)
axios.get('/api/user', { retry: {
count: 3,
delay: 1000
}})
api.getUser({data, retry: true })
api.getUser({data, retry: {
count: 3,
delay: 1000
}})
loading 加载状态
处理loading状态 当请求耗时超过一定时间时,显示loading状态
// 全局设置
axiosLoading(axios, {
handler: (isLoading, loadingText) => {},
delay: 200,
tip: '加载中'
})
axios.get('/api/user', { loading: true })
// 单个请求设置,自定义 loading 显示
api.login({data, loading: {
tip: '正在登录',
handler: (isLoading, loadingText) => {
this.loading = isLoading;
},
}})
// 或设置为 true, 则调用全局配置的 updateLoading 方法
api.login({data, loading: true })
jwtHeader 请求带上JWT Header
请求时自动带上 JWT Token
jwtHeader(axios, {
// 请求头中带上 Authorization
authTokenHeaderName: 'Authorization',
// 从响应头中获取更新的 authToken
newTokenHeaderName: 'newToken',
tokenPrefix: 'Bear',
// 获取 token
getToken: () => {},
// 保存 token
saveToken: token => {}
})
catchError 捕获异常
// 全局设置
axiosLoading(axios, {
// 设置为 true, 则出现异常时,会返回一个一直在 pending 状态的promise 以中断后面的then或catch的执行
// error.type: network、timeout、server、unkonw、cancel
// error.data: 错误信息
handler: (error) => {},
})
// 或设置为 false, 则全局不处理异常,当前请求自己处理异常
api.login({data, catchError: false }).catch(error => {
console.log(error)
})
prevCancel 取消上一个相同请求
在具体请求上,通过配置打开,只有当 prevCancel 为 true 或 配置对象时,生效
同一个请求,请求多次时,取消上一个请求,避免重复请求(一般用于订单状态切换) prevCancel(get/head)
// axios 调用
axios.get('/api/user', { prevCancel: true })
// 配置为一个对象
api.getUsers({data, prevCancel: {
// 判断相同请求时,是否判断请求参数,false 则只判断请求url,不判断参数
matchParams: false,
}})
// 配置为一个bool值
api.getUsers({data, prevCancel: true })
实现方式
Axios插件的实现方式主要应用了拦截器的特性,对请求配置和响应数据做拦截处理,下面以 axiosRetry 为例,在请求失败后,重试请求几次,其它插件实现同理
- 定义
axiosRetry插件方法,接收axios实例和默认配置options两个参数 axios.interceptors.request请求拦截器给 config 添加属性 config['aixos-config']: {count: 0} 以记录重试次数axios.interceptors.response响应拦截器拦截异常信息,判断是否需要重试当前请求
const defaultOptions = {
count: 3, // 重试次数
delay: 0, // 每次重试的延迟时间ms
condition: () => true, // 是否需要重试的自定义判断函数,返回boolean
}
export default function axiosRetry(axios: AxiosInstance, options:RetryOption = {}) {
const defOptions = {...defaultOptions, ...options};
axios.interceptors.request.use((config) => {
// 给config上添加属性 aixos-config: {count: 0} 以记录重试次数
config['aixos-config'] = { count: config['aixos-config'].count || 0}
return config;
})
axios.interceptors.response.use(undefined, error => {
const { config, response } = error;
if (!config || !response || !config.retry) return Promise.reject(error);
const errorAllowed = isRetryAllowed(error);
const statusAllowed = isStatusAllowed(response.status);
const { count, delay, condition } = getRequestOptions(config, defOptions);
if (!errorAllowed || !statusAllowed || !condition(error)) return Promise.reject(error);
if (config['aixos-config'].count! >= count){
config['aixos-config'].count = 0;
return Promise.reject(error);
}
config['aixos-config'].count! += 1;
return new Promise((resolve) => setTimeout(() => resolve(axios(config)), delay));
})
}
axiosRetry 插件的实现可以参数npm包 axios-retry 的实现
相关源码 axios-ext