别在重复封装 Axios 和 Interceptors 了

语言: CN / TW / HK

鉴于我一打开掘金就是一篇 xxx axios xxx 二次封装,以及我工作好几年的情况,有感而发。

真的不要在重复造轮子了,为什么?听我说道。

  1. axios 本身 Api 就足够简单易用,拦截器/适配器足够扩展任何功能。
  2. 擅自封装还会使得原有 Axios 受到影响,增加开发成本和理解成本。
  3. 本身就已经支持 Typescript,你要加 TS 配置的话稍微有点知识就知道能覆盖类型了。

那是不是不用封装

不是,我也没有说让大家不要封装,而是不要在重复封装,过度封装,这些重复工作在我看来就不应该太过于操心。

甚至写 API 方法我认为都是很没有必要的(懒是最佳生产力),我会利用 @hairy/api-generator 生成 api / types。

怎么封装才是最合适

  1. 保留原有库(axios)设计
  2. 可拔插,没有也不影响
  3. 简化配置,可扩展
  4. 功能单一原则

举几个栗子,这里以我自己的工具库 @hairy/axios-bearer 作为示范。

自动解构响应体(data assign response)

我相信大家都有遇到过的情况了把,怎么避免 data.data

由于执行顺序规则 axiosWithAssignResponse 应该最先被调用。 - 解构所有返回的 data ts import axios from 'axios' import { axiosWithAssignResponse } from '@hairy/axios-bearer' axiosWithAssignResponse(axios|instance, '*') // 保证类型正确, 假设你的data是 { code: xxx, message: '...', data: xxx } module 'axios' { interface AxiosResponse { code?: number message?: string } } 请求 ts const { data, code, message, config, ...other } = await axios<string>('xxx') // data(string) / code(number)... - 只解构 data ts // 假设你的data是 { data: xxx } axiosWithAssignResponse(axios|instance, ['data']) // 由于本身类型就自带 data,不需要处理类型 请求 ts const { data } = await axios<string>('xxx') // typeof data === 'string' >> true

自定义响应错误(custom error)

ts import axios from 'axios' import { axiosWithCustomError } from '@hairy/axios-bearer' // 请求响应的 code === 0 将响应拦截为错误 axiosWithCustomError(axios, () => { return response.data.code === 0 }) 请求 ts try { await axios('xxx') } catch(error) { console.log(error.response.data.code) // 0 }

请求时携带参数(extra params)

这个请求我就不展示了,相信大部分都能理解

```ts import axios from 'axios' import { axiosWithExtraParams } from '@hairy/axios-bearer' // 直接携带(header) axiosWithExtraParams(axios, { token: '111' }, 'headers') // 回调携带(header) const getExtraParams = () => { return { token: '111' } } axiosWithExtraParams(axios, getExtraParams, 'headers')

// 携带在 params、data axiosWithExtraParams(axios, { foo: 'xxx' }, 'params') axiosWithExtraParams(axios, { foo: 'xxx' }, 'data') ```

请求时携带 loading 处理(loading helper)

这个要做还要考虑多个请求等待问题,所以封装成一个处理函数是最为恰当的,避免在多个项目重复写。

请求携带 loading 处理是一个很偷懒的做法,一般来说 UI 比较有追求或者要求质量的情况下是不会大面积采用的。 ts import axios from 'axios' import { axiosWithLoadingHelper } from '@hairy/axios' axiosWithLoadingHelper( axios, // custom show (config) => {}, // custom hide (config) => {} ) // use axios.get('xxx', { loading: true }) // global use axios.defaults['loading'] = true

以这种思路封装一个失败重复请求

这个例子只是描述这种思路,更建议大家使用这个库 axios-retry

ts const axiosWithErrorRetry = (axios) => { axios.interceptors.response.use(() => ...) }

总结

无论什么功能,普遍都能以这种方式扩展,不局限于我所列的这些功能,我列的这些例子只是供大家参考和思考(一种思路),能理解这个思维就行,你也可以直接用我的工具库。

放眼望去,其实很多库都包含这种设计,例如 Vue 的 .use、node 框架的中间件、plugins 系统这种拔插式的插件扩展方法。

无论什么时候,我们都应该要以某种方法来避免代码冗余,而不是遇到什么问题就 copy 代码。当然,我也不想说的太过,但我认为,假如(我说假如)你在这个行业拼搏好几年了,还在琢磨这种细枝末节的东西,没有点累积只会 copy 或者重复造之前写过的功能,那么你真的要好好思考一下。

说起来现在开源社区这么发达,看到发的 axios 文章却都是包一层的所谓通用方案(这就是所谓的流量密码?),真的蛮令人感到深思的...

最后声明:本人战五渣、懒鬼一个,文采也不好,大家轻喷!