前天有位佬友说他订阅了我博客的RSS,哭死 QAQ 不能在博客理发店了但是更博的动力++
这几天在写一个用Antd Pro起的项目,这位佬友热心地给了我一个关于书写request的代码模板,并且提了一句这些写在app.tsx
中。当时我并未引以为意,习惯性把请求相关的代码写到了src/utils/request
下,写完之后陷入了沉思:然后呢?这个request是要作为实例导出吗?可是它在文件里并没有引用任何请求实例,我只是给它配置了一些参数而已。
随后我跟这位佬友确认了一下,佬友回答说确实写在app.tsx
里配置就能生效,使用就只需要直接引入umi内置的request就可以。不单单是request,app.tsx
里还写了layout相关的配置,依然是写在app.tsx
就生效。
然而这让我产生了一种不真实感,这是怎么配置上的呢?于是我打开官方文档开始研究。
官方文档提到因为构建时无法使用dom,所以有些配置可能需要运行时来配置,一般而言我们都是在src/app.tsx中管理运行时配置
,所以并不需要在src/utils/request.ts
新建一个请求实例进行配置再暴露出去,只用在src/app.tsx
中export const request
并对request
进行配置。
export const request: RequestConfig = {
errorHandler: (error: ResponseError) => {
const { messages } = getIntl(getLocale());
const { response } = error;
notification.error({
description: '您的网络发生异常,无法连接服务器',
message: '网络异常',
});
throw error;
},
};
但这里也只是介绍了用法,还是没说具体怎么配置的,于是我又去翻了umi的仓库源码。最后定位到有关request插件的代码是在umi仓库的packages/plugins/src/request.ts
里
api.addRuntimePluginKey
是指增加运行插件时的key,应该是对应文档里指的运行时进行配置
requestTpl
变量写入了一套利用axios配置request实例的模板字符串,同时动态获取模板字符串中代码所依赖的文件的路径,这样二者合在一起写入新的request.ts
中,动态生成了一个request实例
具体而言,requestTpl
中定义了一个request实例requestInstance
和request实例的配置config
,刚好这个config
的接口类型和我们在项目的app.tsx
中定义的request配置的接口类型同为RequestConfig
。可惜antd-pro没有开源,这里只能进行一个猜测:antd会将项目的app.tsx
中export的request作为config引入,如果config存在,则使用用户自定义的config,否则使用默认的由getPluginManager().applyPlugins生成的config,再利用config生成一个axios实例返回给request实例接收,然后检测config有没有挂载拦截器相关的属性,有则遍历添加到request实例中,最后再将request实例暴露出去,这样就能让用户无感知地直接在项目里import {useRequest} from 'umi'
// request.ts
let requestInstance: AxiosInstance;
let config: RequestConfig;
const getConfig = (): RequestConfig => {
if (config) return config;
config = getPluginManager().applyPlugins({
key: 'request',
type: ApplyPluginsType.modify,
initialValue: {},
});
return config;
};
const getRequestInstance = (): AxiosInstance => {
if (requestInstance) return requestInstance;
const config = getConfig();
requestInstance = axios.create(config);
config?.requestInterceptors?.forEach((interceptor) => {
if(interceptor instanceof Array){
requestInstance.interceptors.request.use((config) => {
const { url } = config;
if(interceptor[0].length === 2){
const { url: newUrl, options } = interceptor[0](url, config);
return { ...options, url: newUrl };
}
return interceptor[0](config);
}, interceptor[1]);
} else {
requestInstance.interceptors.request.use((config) => {
const { url } = config;
if(interceptor.length === 2){
const { url: newUrl, options } = interceptor(url, config);
return { ...options, url: newUrl };
}
return interceptor(config);
})
}
});
config?.responseInterceptors?.forEach((interceptor) => {
interceptor instanceof Array ?
requestInstance.interceptors.response.use(interceptor[0], interceptor[1]):
requestInstance.interceptors.response.use(interceptor);
});
// 当响应的数据 success 是 false 的时候,抛出 error 以供 errorHandler 处理。
requestInstance.interceptors.response.use((response) => {
const { data } = response;
if(data?.success === false && config?.errorConfig?.errorThrower){
config.errorConfig.errorThrower(data);
}
return response;
})
return requestInstance;
};
const request: IRequest = (url: string, opts: any = { method: 'GET' }) => {
const requestInstance = getRequestInstance();
const config = getConfig();
// ...
}
一晚上看不懂一整个庞大的工程,先扔个猜想在这里,以后有能力了再回来勘勘误。还是赶紧把项目写完吧.jpg
小碎碎念:
查看项目umi版本时偶然发现antd-pro版本是5.2.0 -v- 好浪漫的版本号
翻umi仓库时看到了仓库里友情提供的demo,偶然发现有个demo的readme.md似乎是直接把另个demo的readme.md复制过来了,但是忘记改描述了,于是我给umi提了人生中第一个向大型项目提的pr。我一边改readme一边想着,之前也看到过一个学长在博客里写自己向Vue提过一个pr,当时看到这个的我超震惊,后来偶然跟学长聊起来这个,学长说他只是帮忙改文档的错别字哈哈哈哈
一边看源码一边感慨,到底要写多少代码才能架构出这么庞大的项目,完成这么多功能的集成配置,也不知道组里会怎么教新人上手维护这么大一个项目qwq
项目今天又要写不完了QAQ
后续补充:
其实今天在配置项目的request的时候一直报错:
起初完全没明白为什么,然后去源码仓库全局查了一下这个报错的来源
看得出来这个报错是出现在plugin的注册中,报错提示说这个key没有关联的plugin
而我们项目里,我是将request和两个拦截器都写在了app.tsx
中,并且三个变量全部被export,导致出现报错。佬友debug的时候说app.tsx
里不能直接写拦截器,得写到其他文件里再引用回来
所以我对这部分有了进一步的猜想:项目中app.tsx
中被export的变量会被当做plugin的key名,antd pro会遍历这些变量并找到对应的插件进行注册,项目中的变量request
就是这样作为一个key被捕捉到了,如此就能获取到用户给变量request
的配置,再将其关联到umi的request方法中,这样在逻辑上终于是说得通的了