鉴于我一打开掘金就是一篇 xxx axios xxx 二次封装,以及我工作好几年的情况,有感而发。
真的不要在重复造轮子了,为什么?听我说道。
- axios 本身 Api 就足够简单易用,拦截器/适配器足够扩展任何功能。
- 擅自封装还会使得原有 Axios 受到影响,增加开发成本和理解成本。
- 本身就已经支持 Typescript,你要加 TS 配置的话稍微有点知识就知道能覆盖类型了。
那是不是不用封装
不是,我也没有说让大家不要封装,而是不要在重复封装,过度封装,这些重复工作在我看来就不应该太过于操心。
怎么封装才是最合适
- 保留原有库(axios)设计
- 可拔插,没有也不影响
- 简化配置,可扩展
- 功能单一原则
举几个栗子,这里以 axios-with-extra 作为示范。
自动解构响应体(data assign response)
我相信大家都有遇到过的情况了把,怎么避免 data.data
由于执行顺序规则 withAssignResponse 应该最先被调用。
- 解构所有返回的 data
import axios from 'axios'
import { withAssignResponse } from 'axios-with-extra'
withAssignResponse(axios|instance, '*')
// 保证类型正确, 假设你的data是 { code: xxx, message: '...', data: xxx }
module 'axios' {
interface AxiosResponse {
code?: number
message?: string
}
}
复制代码
请求
const { data, code, message, config, ...other } = await axios<string>('xxx')
// data(string) / code(number)...
复制代码
- 只解构 data
// 假设你的data是 { data: xxx }
withAssignResponse(axios|instance, ['data'])
// 由于本身类型就自带 data,不需要处理类型
复制代码
请求
const { data } = await axios<string>('xxx')
// typeof data === 'string' >> true
复制代码
自定义响应错误(custom error)
import axios from 'axios'
import { withErrorCustom } from 'axios-with-extra'
// 请求响应的 code === 0 将响应拦截为错误
withErrorCustom(axios, () => {
return response.data.code === 0
})
复制代码
请求
try {
await axios('xxx')
} catch(error) {
console.log(error.response.data.code) // 0
}
复制代码
请求时携带参数(extra params)
这个请求我就不展示了,相信大部分都能理解
import axios from 'axios'
import { withExtraParams } from 'axios-with-extra'
// 直接携带(header)
withExtraParams(axios, { token: '111' }, 'headers')
// 回调携带(header)
const getExtraParams = () => {
return { token: '111' }
}
withExtraParams(axios, getExtraParams, 'headers')
// 携带在 params、data
withExtraParams(axios, { foo: 'xxx' }, 'params')
withExtraParams(axios, { foo: 'xxx' }, 'data')
复制代码
请求时携带 loading 处理(loading helper)
这个要做还要考虑多个请求等待问题,所以封装成一个处理函数是最为恰当的,避免在多个项目重复写。
请求携带 loading 处理是一个很偷懒的做法,一般来说 UI 比较有追求或者要求质量的情况下是不会大面积采用的。
import axios from 'axios'
import { withLoadingHelper } from 'axios-with-extra'
withLoadingHelper(
axios,
// custom show
(config) => {},
// custom hide
(config) => {}
)
// use
axios.get('xxx', { loading: true })
// global use
axios.defaults['loading'] = true
复制代码
以这种思路封装一个失败重复请求
这个例子只是描述这种思路,更建议大家使用这个库 axios-retry
const withErrorRetry = (axios) => {
axios.interceptors.response.use(() => ...)
}
复制代码
总结
无论什么功能,普遍都能以这种方式扩展,不局限于我所列的这些功能,我列的这些例子只是供大家参考和思考(一种思路),能理解这个思维就行,你也可以直接用我的工具库。
放眼望去,其实很多库都包含这种设计,例如 Vue 的 .use、node 框架的中间件、plugins 系统这种拔插式的插件扩展方法。
无论什么时候,我们都应该要以某种方法来避免代码冗余,而不是遇到什么问题就 copy 代码。当然,我也不想说的太过,但我认为,假如(我说假如)你在这个行业拼搏好几年了,还在琢磨这种细枝末节的东西,没有点累积只会 copy 或者重复造之前写过的功能,那么你真的要好好思考一下。
说起来现在开源社区这么发达,看到发的 axios 文章却都是包一层的所谓通用方案(这就是所谓的流量密码?),真的蛮令人感到深思的...
最后声明:本人战五渣、懒鬼一个,文采也不好,大家轻喷!
产出
在编写文章的时候,我认为既然如此,不如就分享到底,将我工作之余收集的 axios 作为一个扩展供大家使用或参考,所以我编写了 axios-with-extra,目前有 10 个扩展功能,包含单元测试,后续会在此继续完善文档与扩展方法。