记录 各个配置对应的源码位置
参考
文档
待研究
参考 《api.register api.registerMethod》
对象
hook
但是插件的代码本质其实只是调用 api 进行各种 hook 的注册,而 hook 的执行并非在此阶段执行,因此这里叫插件的注册
modifyAppData hook
onCheck hook
onStart hook
所以,文档中说的hook,其实就是 插件api文档上说的 扩展方法
扩展方法
与 hook 同义
api 上注册一个方法
register() 注册的 hooks 供 applyPlugins 使用
registerMethod() 接收一个 key 和 一个 fn,它会在 api 上注册一个方法
Umi 提供给插件的 api (PluginAPI), 绝大多数都是依靠 registerMethod() 来实现的
插件机制(以addLayouts为例)
addLayouts为例
在这里 packages\preset-umi\src\registerMethods.ts
注册;1
2
3
4
5[
'addLayouts',
].forEach((name) => {
api.registerMethod({ name });
});
在这里 packages\preset-umi\src\features\tmpFiles\routes.ts
调用1
2
3
4
5
6
7
8
9const layouts = await opts.api.applyPlugins({
key: 'addLayouts',
initialValue: [
existsSync(absLayoutPath) && {
id: '@@/global-layout',
file: absLayoutPath,
},
].filter(Boolean),
});
在这里 packages\plugin-docs\src\index.ts
注入事件1
2
3
4
5
6
7
8api.addLayouts(() => {
return [
{
id: 'docs-layout',
file: withTmpPath({ api, path: 'Layout.tsx' }),
},
];
});
applyPlugins register registerMethod 前置知识
register 与 applyPlugins 前置知识:参考 Umi 插件的机制及其生命周期 - register() 、 registerMethod() 以及 applyPlugins()
on modify add 事件key区别
register 接收key,给此key标识注册 事件;
applyPlugins 接收key,触发此key标识下的所有事件;
并且根据 key 的写法不同分为三类:1
2
3
4
5
6
7
8
9
10
11
12
13
14// packages\core\src\service\service.ts
if (!type) {
if (opts.key.startsWith('on')) {
type = ApplyPluginsType.event;
} else if (opts.key.startsWith('modify')) {
type = ApplyPluginsType.modify;
} else if (opts.key.startsWith('add')) {
type = ApplyPluginsType.add;
} else {
throw new Error(
`Invalid applyPlugins arguments, type must be supplied for key ${opts.key}.`,
);
}
}
applyPlugins 执行事件的结果为:
可通过三种方式注册
api.register
1 | api.register({ |
api.registerMethod 方式一
1 | // 原理与上 api.register 相同 |
api.registerMethod 其源码在,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// packages\core\src\service\pluginAPI.ts
registerMethod(opts: { name: string; fn?: Function }) {
assert(
!this.service.pluginMethods[opts.name],
`api.registerMethod() failed, method ${opts.name} is already exist.`,
);
this.service.pluginMethods[opts.name] = {
plugin: this.plugin,
fn:
opts.fn ||
// 这里不能用 arrow function,this 需指向执行此方法的 PluginAPI
// 否则 pluginId 会不会,导致不能正确 skip plugin
function (fn: Function | Object) {
// @ts-ignore
// // 原理与上 api.register 相同,因为 registerMethod 最终走到 register
this.register({
key: opts.name,
...(lodash.isPlainObject(fn) ? (fn as any) : { fn }),
});
},
};
}
api.registerMethod 方式二
api.registerMethod 注册的时候,没有定义fn,此时可通过 api[key](fn)
的方式。1
2
3
4// 原理与上 api.register 相同
api.registerMethod({
name: 'foo',
});
其原理是, api[key]
中的 api 就是 proxyPluginAPI 对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// packages\core\src\service\pluginAPI.ts
static proxyPluginAPI(opts: {
pluginAPI: PluginAPI;
service: Service;
serviceProps: string[];
staticProps: Record<string, any>;
}) {
return new Proxy(opts.pluginAPI, {
get: (target, prop: string) => {
// api[key] 时,最终去 opts.service.pluginMethods 寻找
if (opts.service.pluginMethods[prop]) {
return opts.service.pluginMethods[prop].fn;
}
....
},
});
}
而 opts.service.pluginMethods
就是 第一步 registerMethod 创建的。1
2
3
4
5
6
7
8
9
10
11
12
13// packages\core\src\service\pluginAPI.ts
registerMethod(opts: { name: string; fn?: Function }) {
this.service.pluginMethods[opts.name] = {
fn:
opts.fn ||
function (fn: Function | Object) {
this.register({
key: opts.name,
...(lodash.isPlainObject(fn) ? (fn as any) : { fn }),
});
},
};
}
modifyRendererPath示例
api.applyPlugins({key: ‘modifyRendererPath’})
1 | const rendererPath = winPath( |
如上,想获取 rendererPath 路径,
然后触发 一个hooks或plugin 【modifyRendererPath】,
modifyRendererPath 语义看出是 修改rendererPath路径;
也就是说,umi 定义了插件的 扩展方法 modifyRendererPath ;
彩蛋:插件的 id 和 key
彩蛋:插件的配置
在配置里通过 presets 和 plugins 配置插件,比如:
参考 插件的配置1
2
3
4export default {
presets: ['./preset/foo','bar/presets'],
plugins: ['./plugin', require.resolve('plugin_foo')]
}
配置的内容为插件的路径。