Umi4插件开发 - 最简最快上手的 Umi 插件 Api 快速指南
上一篇文章中我们简单的演示了插件的执行机制,糟糕的演示代码中,我们定义了两个简单的生命周期,而在 Umi 中会有更加丰富的插件生命周期,但我们要开始开发 Umi 插件,并不需要了解和掌握所有的生命周期。这篇文章会告诉你掌握多小的知识点,就可以开始动手开发一个 Umi 插件。
插件的加载
Umi@4 的插件的加载,现在提供三种方式。
约定的 plugin 入口
Umi 体系中约定了根目录下存在 plugin
文件,作为本地插件的约定入口,只要存在这个文件,就会被挂载。
比如我们在 learn-for-umi-plugin
项目中,新建一个文件。
echo "import { IApi } from 'umi';\n\nexport default (api: IApi) => {\n\tapi.onStart(()=>{\n\t\tconsole.log('Local Plugin');\n\t});\n};" > plugin.ts
上述的操作就是,新建一个文件 plugin.ts
然后写一些代码。
你可以根据上一篇文章的提到的步骤,新建一个。或者直接下载归档的源码learn-for-umi-plugin
然后执行项目启动,pnpm dev
或者 npx umi dev
✗ npx umi dev
Local Plugins
info - Umi v4.0.11
info - [MFSU] restore cache
ready - App listening at http://localhost:8000
event - [Webpack] Compiled in 623 ms (496 modules)
info - [MFSU] skip buildDeps
wait - [Webpack] Compiling...
event - [Webpack] Compiled in 5
这时候你就可以用来判断插件是否被加载了。这个最简单的 plugin 是我经常使用到的,判断插件是否被挂载的范例。
你也可以通过umi plugin list
的命令来判断,插件是否被挂载。
✗ npx umi plugin list
...
- ./plugin.ts (from local)
配置文件 plugins
在 config/config.ts
中显示配置当前项目使用到的插件。
mkdir config
echo "import { defineConfig } from 'umi';\n\nexport default defineConfig({\n\tplugins: [],\n});" > config/config.ts
新建一个新的插件文件
echo "import { IApi } from 'umi';\n\nexport default (api: IApi) => {\n\tapi.onStart(()=>{\n\t\tconsole.log('Config Plugin');\n\t});\n};" > config/plugin1.ts
在配置中使用它,修改 config/config.ts
import { defineConfig } from 'umi';
export default defineConfig({
- plugins: [],
+ plugins: [require.resolve('./plugin1'),],
});
✗ npx plugin list
...
- ./plugin.ts (from local)
- ./config/plugin1.ts
通过 presets 加载插件
presets
就是插件集合,所以你可以通过在插件集中配置 plugins,这个效果和直接在配置中配置 plugins
是一样的。
import type { IApi } from 'umi';
export default (api: IApi) => {
const plugins = [
require.resolve('@alita/plugins/xxxx'),
require.resolve('@alita/plugins/yyyy'),
require.resolve('@alita/plugins/zzzz'),
];
return {
plugins,
};
};
需要注意的是 presets
也需要在 presets
配置中配置使用。如:
// config/config.ts
import { defineConfig } from 'umi';
export default defineConfig({
presets: [require.resolve('./preset'),],
});
插件集 presets
看完后面的内容就会很清晰,这里看不明白或者觉得有点绕可以先忽略。
插件 Api
每个插件的作用,我们会在后续的课程中展开讲解。这里不会做详细的扩展。此刻你只需要知道插件 Api 使用过插件传入参数 api
提供调用即可。
如我们上面提到的 api.onStart
就是其中的一个 Api ,也相映对应了一个 Umi 的生命周期,“在项目启动时”。
今天我们先对他们有一个粗略的了解。Umi 的 Api 主要分为三类。核心方法
,扩展方法
和 属性
。
如果你按着三类去看 Umi 的插件 Api 的话,可能还会对你产生困扰。我这边提供一个新的角度,面向新手更加友好。
属性
属性就是提供的一些全局数据,在你需要的时候,直接去取就行,后续会详细介绍,这里不做过多讲解。
比如:api.paths,在 onStart
中打印它。
import { IApi } from "umi";
export default (api: IApi) => {
api.onStart(() => {
console.log("Local Plugin");
console.log(api.paths);
});
};
Local Plugin
{
cwd: '/Users/congxiaochen/Documents/learn-for-umi-plugin',
absSrcPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/src',
absPagesPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/src/pages',
absApiRoutesPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/src/api',
absTmpPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/src/.umi',
absNodeModulesPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/node_modules',
absOutputPath: '/Users/congxiaochen/Documents/learn-for-umi-plugin/dist'
}
需要注意的一点是,所有的属性一般都只能在 hooks 里面使用。也就是需要在 api.xxxxx(()=>{ })
的回调中使用。因为有些属性是在注册阶段不存在的,如果你直接使用这些属性,则会产生意料之外的错误。
除了属性之外的其他 Api 差不多都是通过 api.xxxxx(()=>{ })
注册的。这里我们可以简单的对它们进行分类。
api.registerXXXX
以 register
开头的这部分 Api 多是用于“注册”某些东西,比如注册一个命令 registerCommand
,注册一个微生成器 registerGenerator
。
大多数是传入一个对象,并且这个对象中有一个值,比如 key 或者 name 来作为唯一标识。
api.registerXXXX({ key: string, name: string,})
比如注册一个命令:
api.registerCommand({
name: 'hello',
fn() {
console.log('Hello Umi Developer')
},
});
执行之后就会多一个命令,你可以在你的项目中使用 umi hello
。像官方提供的方法如 umi dev
,umi build
都是以这个方式添加的。
api.addXXXXX
以 add
开头的 Api 是为了增加某个东西的,大部分属于增加数据类的接口。这意味着每个接口都会对应着一个“原始数据对象”。比如你需要增加一个 babel 插件配置,你就可以使用 addBeforeBabelPlugins
。在主入口增加一些代码,你可以用 addEntryCode
。给项目加一个全局的布局组件 addLayouts
。
这部分的理解成本比较低,有部分 api 提供的方法,通过配置文件中提供的配置也可以实现,通过 api 的方式,就可以更自由一点,比如你可以按条件增加某个"东西"。
这里有个原则,“如果你不知道这是什么东西,说明你用不到它”。这会让你在阅读这部分官方文档的时候,无比的舒畅。
api.modifyXXXX
以 modify
开头的 Api 大多数是为了修改某个东西,如果你理解了上面的 add
开头的 Api ,那 modify
开头的 Api 作用类似。最大的差别是以 modify
开头的 Api 大多数会传入一个原始对象,你修改完之后,需要 return 回来。
比如,修改配置信息 modifyConfig
,我最喜欢的用法,就是把 Umi 配置的一些默认值,改成我想要的。
const configDefaults = {
history: { type: 'hash' },
targets: {
ie: 9,
},
hash: true,
model: {},
request: {},
displayName: 'alita-demo',
...api.userConfig,
};
api.modifyConfig((memo: any) => {
Object.keys(configDefaults).forEach((key) => {
memo[key] = configDefaults[key];
});
return memo;
});
需要特别关注的点是传入 memo
和 return memo
。
api.modifyConfig((memo: any) => {
// some beautiful code
return memo;
});
理解了上述内容,那 Umi 的 70 个 Api 在你的眼里就是
api.registerXXXX({name:'唯一值',fn:()=>{ '干些啥事' }});
api.addXXXXX(()=>{ return '返回要添加的一些东西'});
api.modifyXXXX((memo)=>{ memo.some = '一些想要修改的东西'; return memo;});
if(api.属性 === 'xxx') { '当某个条件时,我想做一些事情。'}
// 有一个方法类,我需要获取一点数据
someUtil(api.属性)
这应该算是最简最快上手的 Umi 插件 Api 快速指南了。
下一篇内容正式来回答这个系列的初衷:我要怎么写一个 Umi 插件?
这一篇文章间隔 9 天更新的原因是 “上一篇文章阅读到 1000 才更新”。
感谢阅读,如果你对这个内容感兴趣,可以关注这个专栏:Umi 插件开发。如果你觉得这个文章对你有帮助,请点赞评论收藏支持我,并将这个文章分享给更多的朋友,文章的数据是我持续更新的动力。感谢。
转载自:https://juejin.cn/post/7133149810936250398