1. 前言
在我往日的开发中,经常用到的请求工具就是axios,无论是在写react,还是vue,又或者是在react-natvie,我一直都使用的是axios。当然,在项目初期,我一般也是直接使用axios的全局对象进行请求,例如:
import axios from 'axios';
const resData = await axios.get('/login',{ account: 'xxxx', password:'xxx' });
。。。。。。
当然确实能用,但是如果在ts中,我想使api有良好的类型提示,那我就得这么写:
import axios from 'axios';
type LoginRes = {
userInfo: {
avatar:string;
}
}
const resData = await axios.get<LoginRes>('/login',{ account: 'xxxx', password:'xxx' });
。。。。。。
当然,登录的api都一般只用一次就行,但是比方说如果有一些特殊的api,要在多处调用,那就不得不每次都这么写一遍:
type GetMoneyRes = {
money:account
}
// user-info.vue //这里要调用
import axios from 'axios';
const resData = await axios.get<GetMoneyRes>('/GetMoneyRes');
// account-info.vue //这里要调用
import axios from 'axios';
const resData = await axios.get<GetMoneyRes>('/GetMoneyRes');
。。。。。。
而且,如果api 发生更改,大量的修改对我们来说也是一种痛苦,所以,一个全局的api 管理是至关重要的。
2. 设计
我的设计思路是,给每个类型的api都做一个单独的axios实例,这样方便扩展和更改。当然我发现,在react-native中,一直使用同一个axios实例,有时候会出现cookie丢失的情况,这种设计也就完美的解决了这个问题。然后将所有的api实例都汇集到一个大的实例当中。这样就能够在react、或者vue中使用context或者provide/inject在根组件注入一个全局api实例,这样就能方便所有组件调用。设计图如下:
3. 实现
3.1 写一个方便打入axios的装饰器
当然,为了方便给每一个api实例打入axios实例,我使用一个装饰器:
// api.ts
import axios from 'axios';
import { defaultConfig } from '../config';
import { requestInterceptors } from '../interceptors/request';
import { responseError, responseSussess } from './../interceptors/response';
type ApiParams = {
host?: string; //
prefix: string;
};
export const Api = ({ host, prefix }: ApiParams) => {
return function <T extends { new (...args: any[]) }>(constructor: T) {
const hostUrl =
host === undefined ? (import.meta.env.VITE_API_BASE_URL as string) : ''; // vite下的开发环境配置和正式环境url配置
const axiosInstance = axios.create({
...defaultConfig,
baseURL: hostUrl + prefix,
});
axios.interceptors.response.use(responseSussess, responseError);
axios.interceptors.request.use(requestInterceptors);
return class extends constructor {
http = axiosInstance;
};
};
};
// config.ts
import qs from 'qs';
import { AxiosRequestConfig } from 'axios';
export const defaultConfig: AxiosRequestConfig = {
withCredentials: true,
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'repeat' });
},
};
当然,这里的qs是处理数组参数的时候用的,这里就不展开了。
3.2 创建一个api
然后我们使用这个装饰器,写出一个OAth的api:
// oath.ts
import { AxiosInstance } from 'axios';
import { Api } from '../decorators/api';
type LoginRes = {
userInfo: {
avatar: string;
};
};
@Api({ prefix: 'OAth' })
export class OAth {
private http!: AxiosInstance;
/**
* 登录
*
* @param {string} account
* @param {string} password
* @return {*}
* @memberof OAth
*/
async login(account: string, password: string) {
return (
await this.http.post<LoginRes>('login', {
account,
password,
})
).data;
}
}
然后将这个api,放入总的api实例中进行管理:
// api-instance.ts
import { OAth } from './apis/login';
export class ApiInstance {
readonly oath = new OAth();
}
3.3 写一个hook,并将实例注入到根组件
然后,我们就可以使用provide/inject在根组件注入,并暴露出一个hook:
// api-instance.ts
。。。。。。
const key: InjectionKey<ApiInstance> = Symbol();
export const installApi: (app: App, ...options: any[]) => any = app => {
app.provide<ApiInstance>(key, new ApiInstance());
};
export const useApi = () => {
return inject<ApiInstance>(key) as ApiInstance;
};
。。。。。。
// main.ts
。。。。。。
import { installApi } from './http/api-instance';
createApp(App).use(router).use(installApi).mount('#app');
。。。。。。
这样,就能在所有的组件当中使用这个hook了:
当然,react也一样,使用context注入就ok了。
代码链接(仅供参考): radium-vue-admin/admin-frontend