likes
comments
collection

Umi4插件开发 - 最简最快上手的 Umi 插件 Api 快速指南

作者站长头像
站长
· 阅读数 12

上一篇文章中我们简单的演示了插件的执行机制,糟糕的演示代码中,我们定义了两个简单的生命周期,而在 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` 项目

你可以根据上一篇文章的提到的步骤,新建一个。或者直接下载归档的源码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 devumi 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 插件开发。如果你觉得这个文章对你有帮助,请点赞评论收藏支持我,并将这个文章分享给更多的朋友,文章的数据是我持续更新的动力。感谢。