在 Umi 中,插件实际上就是一个 JS 模块,你需要定义一个插件的初始化方法并默认导出。如下示例:
export default (api) => {// your plugin code here};
需要注意的是,如果你的插件需要发布为 npm 包,那么你需要发布之前做编译,确保发布的代码里面是 ES5 的代码。
该初始化方法会收到 api
参数,Umi 提供给插件的接口都是通过它暴露出来的。
以下我们通过完成一个简单的需求,来进一步了解 Umi 的插件开发
Umi 约定式路由中的表现是主路由,对应到 index
路由,即访问 http://localhost:8000
实际上访问到的页面是 src/pages/index
,有时候我们在开发过程中会遇到,希望修改主路由的情况,比如希望路由 /
访问的是 src/pages/home
。
你可以通过 create-umi 直接创建一个 Umi 插件的脚手架:
yarn create umi --plugin? Select the boilerplate type plugin? What's the plugin name? umi-plugin-main-path? What's your plugin used for? config umi main path? What's your email? 448627663@qq.com? What's your name? xiaohuoni? Which organization is your plugin stored under github? alitajs? Select the development language TypeScript? Does your plugin have ui interaction(umi ui)? Nocreate package.jsoncreate .editorconfigcreate .fatherrc.tscreate .gitignorecreate .prettierignorecreate .prettierrccreate CONTRIBUTING.mdcreate example/.gitignorecreate example/.umirc.tscreate example/app.jsxcreate example/app.tsxcreate example/package.jsoncreate example/pages/index.csscreate example/pages/index.jsxcreate example/pages/index.tsxcreate example/tsconfig.jsoncreate example/typing.d.tscreate README.mdcreate src/index.tscreate test/fixtures/normal/pages/index.csscreate tsconfig.json✨ File Generate Done
$ yarn
你也可以使用 npm install ,因为有编写测试,所以安装了 puppeteer,如果你安装失败,可能需要科学上网,或者使用淘宝源。
在 Umi@3 中,当插件使用 @umijs
或者 umi-plugin
开头,只要安装就会被默认使用,所以如果你的插件名以上述规则命名,你就不需要在 config 文件中显式使用你的插件,如果你的插件命名不满足上述规则,那你只需要在 config 中显示使用即可。
import { defineConfig } from 'umi';export default defineConfig({plugins: ['you-plugin-name'],});
此次示例中我们的插件名是 umi-plugin-main-path 。
首先我们先看一下,初始化脚手架中的代码,如果使用这个插件,那么就会打印日志 use plugin
,然后使用 modifyHTML
api 在 body
上添加了 h1
的内容。更多插件 api ,请查阅Plugins Api。
export default function (api: IApi) {api.logger.info('use plugin');api.modifyHTML(($) => {$('body').prepend(`<h1>hello umi plugin</h1>`);return $;});}
为我们的插件增加一个配置,使用 describe 注册配置。
api.describe({key: 'mainPath',config: {schema(joi) {return joi.string();},},});
增加我们插件的主逻辑
if (api.userConfig.mainPath) {api.modifyRoutes((routes: any[]) => {return resetMainPath(routes, api.config.mainPath);});}
这里需要注意的是,我们在判断时取的是 api.userConfig,而在 api 的回调中使用的是 api.config,你可以理解为 api.userConfig 是配置中的值, api.config 是插件修改后的值,这里可以是任意插件修改。
在演示中使用我们的插件:
在 example/.umirc.ts
中增加配置
import { defineConfig } from 'umi';export default defineConfig({plugins: [require.resolve('../lib')],mainPath:'/home'});
新建 page 页面,新建 example/pages/home.tsx
import React from 'react';import styles from './index.css';export default () => (<div className={styles.normal}><h2>Home Page!</h2></div>);
查看效果
yarn startStarting the development server...✔ WebpackCompiled successfully in 20.73sApp running at:- Local: http://localhost:8000 (copied to clipboard)- Network: http://192.168.50.236:8000
浏览器访问 http://localhost:8000
就可以访问到 home
,要访问之前的 index
页面,要通过 http://localhost:8000/index
。
一般 Umi 插件的测试,我们都是采用结果测试的方案,只看最终运行效果。这里我们使用的是 test-umi-plugin
,它也有一定的约定,指定 fixtures
之后,他会自动执行文件夹下的 test 文件。
在 test/fixtures/normal/.umirc.ts
中增加配置
export default {plugins: [require.resolve('../../../lib')],mainPath: '/home'}
新建 page 页面,新建 test/fixtures/normal/pages/home.tsx
import React from 'react';import styles from './index.css';export default () => (<div className={styles.normal}><h2>Home Page!</h2></div>);
修改测试用例 test/fixtures/normal/test.ts
export default async function ({ page, host }) {await page.goto(`${host}/`, {waitUntil: 'networkidle2',});const text = await page.evaluate(() => document.querySelector('h1').innerHTML,);expect(text).toEqual('Home Page');};
执行测试
$ yarn test$ umi-testconsole.log[normal] Running at http://localhost:12401PASS test/index.e2e.ts (10.219s)✓ normal (1762ms)Test Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 11.057sRan all test suites.✨ Done in 14.01s.
本次示例的完整代码在 umi-plugin-main-path。