直接上代码
根据 umi官网 介绍可知,umi/max仅提供一种运行时配置的方法。
如果是搭配typescript开发的话,最好使用@umi/max提供的RequestConfig类型进行字段控制。
因为是在app.ts中添加的配置,但是并不知道该配置是在何时何地如何被使用的,所以去翻阅了一下umijs/plugin-request源码
看到index.ts文件后,得到了启发:
import { IApi } from 'umi-types';
import { join, dirname } from 'path';
import assert from 'assert';
import { readFileSync } from 'fs';
export interface RequestOptions {
dataField?: string;
}
export default function(api: IApi, options: RequestOptions) {
api.addRuntimePluginKey('request');
const { dataField = 'data' } = options || {};
const source = join(__dirname, '..', 'src', 'request.ts');
const requestTemplate = readFileSync(source, 'utf-8');
const namespace = 'plugin-request';
assert(/^[a-zA-Z]*$/.test(dataField), 'dataField should match /^[a-zA-Z]*$/');
api.onGenerateFiles(() => {
try {
// Write .umi/plugin-request/request.ts
let formatResultStr;
if (dataField === '') {
formatResultStr = 'formatResult: result => result';
} else {
formatResultStr = `formatResult: result => result?.${dataField}`;
}
api.writeTmpFile(
`${namespace}/request.ts`,
requestTemplate
.replace(/\/\*FRS\*\/(.+)\/\*FRE\*\//, formatResultStr)
.replace(/\['data'\]/g, dataField ? `['${dataField}']` : '')
.replace(/data: T;/, dataField ? `${dataField}: T;` : '')
.replace(
/umi-request/g,
api.winPath(dirname(require.resolve('umi-request/package'))),
)
.replace(
/@ahooksjs\/use-request/g,
api.winPath(dirname(require.resolve('@ahooksjs/use-request/package'))),
),
);
} catch (e) {
api.log.error(e);
}
});
// 这里,他去添加了Umi导出的配置
// 看代码,猜测路径为: .umi/plugin-request/request.ts
/**
* umi 允许插件添加需要 umi 额外导出的内容。
* 流程是这样的,umi 脚本执行的时候,触发 onGenerateFiles hook 生成临时文件。
* umi 预设的插件集中有个叫做 umiExports 的插件,
* 该插件会触发 addUmiExports hook,执行所有插件的该方法,
* 获得插件中指定的额外导出内容,将这些内容放到临时文件中。
*/
api.addUmiExports([
{
exportAll: true,
source: api.winPath(join(api.paths.absTmpDirPath, namespace, 'request')),
},
]);
}
找到了找到了,在./src/.umi/plugin-request/request.ts中找到了答案:
// .umi/plugin-request/request.ts
let config: RequestConfig;
const getConfig = (): RequestConfig => {
if (config) return config;
config = getPluginManager().applyPlugins({
key: 'request',
type: ApplyPluginsType.modify,
initialValue: {},
});
return config;
};
// .umi/core/plugin.ts
import * as Plugin_0 from 'C:/workspace/psq_system/src/app.ts';
export function getPlugins() {
return [
{
// Plugin_0就是{antd:{}, layout:{}, request: {}}
apply: __defaultExport(Plugin_0),
// 这里这里!
path: process.env.NODE_ENV === 'production' ? void 0 : 'xxx/src/app.ts',
},
// ....
];
}
// 这里就是上面用到的keys,也就是app.ts中可以设置的内容
export function getValidKeys() {
return ['patchRoutes','patchClientRoutes','modifyContextOpts','modifyClientRenderOpts','rootContainer','innerProvider','i18nProvider','accessProvider','dataflowProvider','outerProvider','render','onRouteChange','antd','getInitialState','layout','qiankun','request',];
}
let pluginManager = null;
export function createPluginManager() {
pluginManager = PluginManager.create({
plugins: getPlugins(),
validKeys: getValidKeys(),
});
return pluginManager;
}
export function getPluginManager() {
return pluginManager;
}
通过上面的代码,我们可以了解.umi/plugin-request/request.ts中的config是如何生成的。
- .umi/plugin-request/request.ts 就是umi运行时读取的配置项
- umi在注册plugins的时候第一个就是读取app.ts的配置,赋值给Plugin_0
- Plugin_0就是
{ antd: {}, layout: {}, request: { }, .... }
- 将
{ apply: Plugin_0, path: "./src/app.ts" }
作为参数plugins的其中一个,用来创建umi的PluginManager - 创建时会遍历plugins,以第一个举例,拿到他的apply,也就是Plugin_0,遍历对象,拿到key,放入pluginManager的hooks中。
- 而
getPluginManager().applyPlugins({})
就是对某个hooks的扩展 - 而.umi/plugin-request/request.ts中的config就是pluginManager.hooks.request
最后再来看一下.umi/plugin-request/request.ts中对axios的实例化,看一下config的使用
const request: IRequest = (url: string, opts: any = { method: 'GET' }) => {
// 这里需要注意!
const requestInstance = getRequestInstance();
// 这里的config就是获取到的pluginManage中的hooks.request
const config = getConfig();
requestInstance = axios.create(config);
// 这里就是对app.ts中request的使用
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);
})
}
});
// ....其他的暂时不重要
}
至此,我终于理顺了app.ts中配置项request是如何使用的了!
大功告成,理了将近5个小时,还是自己太菜了,应该直接去看plugin-request包的,结果先去看了umi中对app.ts的使用,期间又发现对@babel的parse,traverse,types包一脸懵逼,又去稍微了解了一下,花了三个小时,,
最后看plugin-request也才看了一个多小时。以后要对@babel的包也要了解一下了,前端的知识真是太多了。。