在现代前端开发中,HTTP 请求的管理是一个至关重要的功能,尤其是在构建复杂的 Web 应用时。为了提高代码的复用性和维护性,开发者往往会对原生的请求库进行封装,以便提供更为清晰、灵活且可扩展的接口。
在开发中,封装 Axios 是为了提高代码的可复用性、可维护性以及提高对请求和响应的管理能力。通过TypeScript 封装 Axios,我们不仅能够简化 HTTP 请求的使用,还能利用 TypeScript 的类型系统,确保请求和响应数据的类型安全。下面,我们将通过逐步解释如何封装 Axios。
为什么封装 Axios
首先,原始的 Axios API 提供了直接的 HTTP 请求功能,但当项目逐渐增大时,直接使用 Axios 会面临以下问题:
- 重复代码:每次请求都需要编写相同的代码(如处理请求头、错误处理等)。
- 请求拦截和响应拦截:通常需要对请求或响应进行一些统一的处理(如添加 token、错误重试等)。
- 类型安全:在请求和响应过程中,可能会出现类型不一致的情况,影响代码的安全性和可维护性。
- 请求取消:有时需要在请求发送后,但在响应返回之前取消请求,尤其是在单页应用中,组件销毁时常常需要中止当前请求。
通过封装 Axios,所有这些问题都可以得到集中管理和统一处理。
本文将基于一段封装了 Axios 的 Core<T>
类,逐步分析每个代码块的功能,并解释这种封装方式的优点及应用场景。
好的,我们可以更深入地解析这段 Core<T>
类的实现,逐一分析它的设计思路、实现细节以及背后的最佳实践。
Core<T>
类的详细解析
1. 类的基本结构
export default class Core<T = any> {
instance: AxiosInstance;
isCancel: boolean;
constructor(config?: RequestConfig<T, T>, callback?: Function) {
this.instance = axios.create(config);
this.isCancel = !!config?.isCancel;
...
}
}
-
Core<T>
是一个通用的类,它接受一个泛型T
,代表请求和响应数据的类型。这使得该类在处理不同数据类型时,能够保持灵活性和类型安全。 -
类的属性包括:
instance
:一个AxiosInstance
,用来执行实际的 HTTP 请求。isCancel
:一个布尔值,表示是否启用了请求取消功能。
2. Axios 实例的创建
this.instance = axios.create(config);
axios.create(config)
是用来创建一个自定义配置的 Axios 实例。这个实例可以拥有自己独立的配置(如 baseURL、headers、timeout 等),并且所有通过 this.instance
发起的请求都会使用这些配置。
3. 请求取消的机制
请求取消的核心思想是通过拦截器和请求去重机制,避免重复发送相同请求,或者在请求发出后能够及时取消未完成的请求。
if (this.isCancel) {
const requestInterceptor = (config: RequestConfig<T, T>) => {
if (config.isCancel === false) {
return config;
}
pending.remove(config);
pending.add(config);
return config;
};
this.isCancel
为true
时,说明启用了请求取消机制。然后,系统会注册两个拦截器:请求拦截器和响应拦截器,用来处理请求的去重与取消。- 请求拦截器首先检查
config.isCancel
,如果为false
,则跳过取消操作;否则,会将当前请求加入pending
列表,这个列表用于记录正在进行的请求。 pending.remove(config)
用来移除已经发出的请求,pending.add(config)
会将新请求添加到列表中,以便后续的取消操作。
4. 请求拦截器
const requestInterceptor = (config: RequestConfig<T, T>) => {
if (config.isCancel === false) {
return config;
}
pending.remove(config);
pending.add(config);
return config;
};
- 请求拦截器用于在请求发出前对请求配置做一些处理。常见的应用场景包括:修改请求头、添加认证 token、或者在请求重复时取消前一个请求。
- 在这里,
pending.remove(config)
是用来移除之前相同请求,pending.add(config)
则将当前请求添加到待处理的请求队列中。 - 这个机制保证了如果一个请求未完成,且相同的请求已经被发出,那么新的请求会“覆盖”掉旧的请求,避免不必要的重复请求。
5. 响应拦截器
const responseInterceptor = (response: AxiosResponse<T>) => {
pending.remove(response.config);
return response;
};
- 响应拦截器的作用是在收到响应时,对响应数据进行处理或者对请求进行清理。
- 在这里,
pending.remove(response.config)
的作用是将已经完成的请求从pending
列表中移除,确保请求列表不会积压,避免内存泄漏。
6. 错误响应拦截器
const responseInterceptorCatch = (error: AxiosError) => {
if (error.config) {
pending.remove(error.config);
}
return Promise.reject(error);
};
- 错误响应拦截器会捕获请求中的错误(如网络错误、响应超时等),并且移除相关的请求配置。
- 同样,错误也会被传递给调用方,
return Promise.reject(error)
会将错误信息返回,以便进一步处理。
7. 请求执行与返回值
core<R>(config: RequestConfig<T, R>): Promise<R> {
return new Promise((resolve, reject) => {
if (config.interceptors?.requestInterceptor) {
config = config.interceptors.requestInterceptor(config);
}
if (config.isCancel && !this.isCancel) {
pending.remove(config as any);
pending.add(config as any);
}
this.instance
.request<any, R>(config)
.then((res) => {
if (config.interceptors?.responseInterceptor) {
res = config.interceptors.responseInterceptor(res);
}
resolve(res);
})
.catch((err: AxiosError) => {
reject(err);
})
.finally(() => {
if (config.isCancel && !this.isCancel) {
pending.remove(config as any);
}
});
});
}
-
这里定义了一个
core
方法,负责真正执行 HTTP 请求。它通过this.instance.request()
发起请求,并返回一个Promise
,该Promise
会在请求完成后被解决或拒绝。 -
请求配置中的
interceptors.requestInterceptor
和interceptors.responseInterceptor
是可选的用户自定义拦截器。如果传入这些拦截器,core
会在请求发出前和响应接收到后调用它们。 -
在
finally
块中,使用pending.remove(config)
确保无论请求成功还是失败,都将请求从pending
请求列表中移除。
8. 封装常见 HTTP 请求方法
get<R>(url: string, data: object = {}, config: RequestConfig<R, R>): Promise<R> {
return this.core({ ...config, url, params: data, method: 'GET' });
}
post<R>(url: string, data: object = {}, config: RequestConfig<R, R>): Promise<R> {
return this.core({ ...config, url, data, method: 'POST' });
}
delete<R>(url: string, config: RequestConfig<R, R>): Promise<R> {
return this.core({ ...config, url, method: 'DELETE' });
}
- 这些方法封装了 HTTP 请求的不同类型(如 GET、POST、DELETE 等),每个方法都会调用
this.core()
,并根据请求的类型调整请求配置。 url
,data
, 和config
会一起传递给core
方法,构成完整的请求配置。
9. 请求管理与清理
abort(config: RequestConfig<T, T>): void {
pending.remove(config as any);
}
clear(): void {
pending.clear();
}
abort
方法允许用户手动取消一个特定的请求,它通过从pending
列表中移除请求来实现。clear
方法则是清理所有的请求,通常用于页面卸载或者应用退出时,防止有未完成的请求仍然存在。
10. 总结:设计思想与优点
10.1 请求取消与去重
通过 pending
列表和请求拦截器,Core
类实现了请求的去重和取消功能。重复的请求会被排除,避免多次发出相同的请求。这是前端开发中对性能优化的一个重要实践,尤其是在处理复杂的异步操作时。
10.2 灵活的拦截器机制
请求和响应的拦截器提供了极大的灵活性,开发者可以在拦截器中根据需要修改请求或响应。例如,可以在请求拦截器中加入鉴权信息,在响应拦截器中统一处理错误或格式化数据。
10.3 可扩展性与可维护性
通过封装 Axios 的请求,Core<T>
类实现了 HTTP 请求的统一管理,使得网络请求的代码更具可复用性和易维护性。通过简单的接口调用,开发者可以方便地发起各种类型的请求,并且通过配置轻松控制请求行为(如超时、取消、重试等)。
10.4 错误处理与清理
通过在 catch
和 finally
块中处理错误和清理操作,Core<T>
类能够保证请求的完整性。即使请求出错,也能确保资源被及时释放,避免内存泄漏。
总的来说,这种封装提供了非常强大的功能,同时也提高了代码的可维护性和清晰度。对于复杂的前端应用,尤其是在处理大量异步请求时,这种封装能够显著简化请求管理和错误处理的复杂度。
这次是真对我以前项目真对axios 再次优化,目前没有仓库存在,因为最近计划开发一个灵活的微前端系统,把这个请求器放到这个项目里,作为一个基础工具库