前几天在某网站上看到一篇文章,说是用 ts 对 axios 进行了下封装,从点赞量、评论量和访问量上来看,有很多人都看过这篇文章了。 我之前也看过 axios 的源码,也基于 axios 进行过扩展和二次封装。对 axios 的内部原理和使用方式不可谓不熟悉。 虽然很多人在评论里说,收益匪浅啊,写的真棒啊等等,但我通读完整篇文章,得到的结论是: 有的开发者喜欢基于 axios 再在外层封装一层,但这种方式实现的成本太高。 无论是实现跟 axios 一样的功能,还是外层进行简化,然后再按照 axios 的方式传给 axios,都增加了很多开发的成本。如: 如有的开发者封装时,喜欢把 GET 请求方式的 params 字段和 POST 请求方式的 data 字段合并成一个,觉得可以减少使用者对字段记忆的成本。然后组件内部,再根据请求方式,决定传给 axios 的 params 字段还是 就像上面的简要封装,连拦截器、取消请求等功能都给屏蔽掉了。但是,又想用,怎么办?那就靠着自己半吊子的知识,自己再封装一个,然后再跟 axios 进行对接,累不累啊。 有的人说他的要求更复杂,不二次额外封装一层,都没法用,比如: 其实,上面的这几条需求,通过 axios 的拦截器就可以实现了,外面不用再封装一层。 axios 库本身就已经提供了多种的扩展方式,为什么不直接用呢? axios 可以自定义请求适配器 adapter。 很多同学说 fetch 已经成为标准了,为什么 axios 内部还不支持。其实相比 XMLHttpRequest ,fetch 还是多少有点欠缺的,如取消请求的 AbortController 在 IE 因此,在 fetch 还没有对齐 XMLHttpRequest 里的这些功能时,内部还很难使用 fetch 来实现。 若您真的想用 fetch 直接在适配器上进行扩展,而不是基于 axios 再在外层封装一层,原因有几个: 这种方式,对其他开发者也很友好,只要知道 axios 怎么用,就知道你封装的这个怎么用。不用增加学习的成本。 axios 在配置上,可以进行全局的配置和请求的单独配置,同时还有请求拦截器和响应拦截器,对某一类的请求进行处理。 若所有请求的 url 的 baseURL 是相同的,那么就可以在全局配置中进行配置;若某几个接口的 baseUrl 跟别的不一样,那么可以单独对其进行配置,或者在请求拦截器中,直接修改请求的 baseUrl。 这些不同的配置方式,我个人觉得已经可以满足绝大部分的需求了。 axios 源码虽不是用 typescript 编写的,但官方也是提供了 ts 定义的:index.d.ts。 那些上来就说用 typescript 封装 axios 的,你确定不是在搞笑吗? 而且,做为个人开发者,在开发和使用过程中,肯定会产生纰漏和 bug,比不过经过多人验证过的仓库。 其实也不是不能封装,毕竟 axios 做为一个通用的框架,它不可能适应所有的项目和架构。我不希望的是过度的封装,既没必要,又增加后来者的学习成本。 有的同学在 React/Vue 中封装 axios,倒是 更具体的如何在 React 封装一个请求的 hook,可以参考该链接:使用 react 的 hook 实现一个 useRequest。 有的封装,是为了减少项目整体的改造成本,和其他人学习新用法的成本。比如之前我也基于 axios,在外层封装过一个请求库。当时为了跟之前的请求方式保持一致,就在外层额外封了一层。后来就出现当需要扩展功能时,特别麻烦,还不如从一开始设计时,就仅仅扩展他的适配器,或者几个简单的配置就行。 在封装的时候,首先我们要明白 axios 可以完成什么工作,他实现这些功能都有什么意义,为什么可以传入这些字段,又为什么要返回了那么多字段(我明明只需要接口返回的 data)? 想明白这些问题,就知道我们要保留什么,如何进行扩展和封装了。 我在很久之前写过一篇文章:前端工程师如何通过造轮子提高自己,但似乎这篇文章的观点与之前的冲突了。 其实,并不冲突。造轮子是为了通过模仿成熟的作品来提高自己,但真的在实际应用时,我们就要考虑得更多。 自己实现出来的东西,大部分都比不上社区里经过千锤百炼验证过的。而且在使用的过程中,还要考虑减少其他人的学习成本。 比如你就不想用 Vue,觉得 Vue 这个框架优点大,然后选择了一个叫完全没必要
。1. 完全没必要 #
const myAxios = async (config) => {
/**
* 中间各种封装,然后最后再使用axios发起请求1
* */
try {
const { status, data } = await axios(config);
if (status >= 200 && status <= 304) {
return data;
}
} catch (err) {
console.error(err);
}
return null;
};
data 字段。或者有的屏蔽掉对请求的配置,有的屏蔽掉 axios 对外返回的字段。
1.1 方便扩展的适配器 #
浏览器中并没有实现,同时也不支持上传进度和下载进度。
来进行请求,完全可以按照文档来扩展您的适配器。如何实现自定义的适配器,您可以参考这篇文章 如何实现 axios 的自定义适配器 adapter。
1.2 各种形式的配置 #
axios.defaults.timeout = 6000; // 全局配置
/**
* 拦截器中配置,若url中有`aaa`,则过期时间设置为4000ms
**/
axios.interceptors.request.use(
function (config) {
if (/aaa/.test(config.url)) {
config.timeout = 4000;
}
return config;
},
function (error) {
return Promise.reject(error);
},
);
axios('https://www.xiabingbao.com', { timeout: 2000 }); // 单独对某个请求进行配置
1.3 本身就支持 typescript #
2. 不是不能封装 #
可以
有封装的意义,比如在 React 中封装一个请求的简单 hook 等。const useAxios = (config) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [result, setResult] = useState(null);
useEffect(() => {
setLoading(true);
axios(config)
.then(setResult)
.catch(setError)
.finally(() => {
setLoading(false);
});
}, []);
return { loading, error, result };
};
3. 总结 #
mini-vue
的框架来开发项目。就社区完善程度来说,这个 mini 版肯定是比不上官方 Vue 的,后来者还得重新学习 mini 版的语法,当遇到问题时,都不知道去问谁,毕竟这个问题,只有 mini 版里才会有,其他人用的少,解答的人也少。
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论