Umi 插件实战教程

news2025/4/27 3:12:17

61f0291997aa33b04ba79387ae55bd78.png

引言

笔者最近开发了一款 umi 插件:plugin-umi-cmdk[1],该插件的功能主要是:在 umi 项目里可以方便的集成 cmd + k ,实现菜单等搜索。

主体功能并不复杂,但是在集成作为 umi 插件过程中踩了不少坑,主要是 umi 官方文档的, 开发插件 | UmiJS[2]实属写得烂,看完之后根本无法上手。

所以写一篇完整的插件开发教程,手把手上手 umi 插件开发。

准备工作

创建项目

新建一个文件夹umi-plugin-demo,直接通过 umi 的官方模版进行创建

yarn create umi

之后选择模板的时候选择:Umi Plugin

670b971afd6cb8cd13be012bb8ec193b.png
Pasted image 20230326160516

创建 example 目录用于测试

然后创建完之后在 umi-plugin-demo 的根目录新建一个 example 文件夹,用于测试。

将 example 初始化成一个 umi 项目:

cd example
// 然后
yarn create umi

根据你的需求选择一个模板98193d0ccfe2352171e5372d24c3b9f9.png

我选了 Ant Design Pro ,现在整个目录结构大致是这样。c35f5dd56c29066f5831339327ef2e57.png

挂载插件

1、在 src/index.ts 里增加 log

import type { IApi } from 'umi';

export default (api: IApi) => {
  // See https://umijs.org/docs/guides/plugins
  api.onStart(() => {
    console.log("欢迎关注前端桃园!");
  });
};

这个代码的意思就是在插件启动的时候打一个 log「插件开始加载了!!!」。

2、在 example/.umirc 里引入插件

import { defineConfig } from '@umijs/max';
import { join } from 'path';

export default defineConfig({
  plugins: [join(__dirname, '../src/index.ts')],
  // 其他的配置
 })

通过 plugins 这个配置,将插件文件进行引入,在启动 example 项目的时候插件就会被加载

3、查看 logo 然后在 example 下,通过 npm start 启动项目,即可看到控制台的 log。d6dd93480077f1fedfeb46f8c919c990.png

当我们看到控制台输出了想要的日志,到这一步,准备工作已经就绪,接下来就可以开始写插件了。

更多挂载方式

除了通过 plugins 配置项挂载插件,umi 还提供了一种约定式的挂载方式。

umi 体系中,约定根目录下存在 plugin 文件夹作为本地插件的约定入口。只要存在该文件夹,其中的插件就会被自动挂载,无需再进行额外的配置。

例如,在我们的示例中,可以直接在 example 目录下创建一个 plugin.ts 文件,即可将插件挂载到 umi 中,无需在 .umirc 配置文件中添加插件配置。这种方式通常用于本地测试插件。

编写插件

一般的插件机制是通过暴露钩子来实现的,钩子会在运行时执行并提供一些属性供插件开发者使用。插件开发者可以在钩子中实现想要的功能,从而扩展插件的功能。因此,插件开发就是在钩子中编写代码实现自定义功能的过程。

所以一般在编写框架插件的时候就需要先了解一下该框架提供了哪些钩子,这个决定了开发者可以做哪些扩展的事情。

一般开发插件的基本流程如下:1、 浏览 umi 提供的插件 API2、确定插件的目的和功能 3、找到需要的插件 API 和生命周期方法

功能分析

我以我写的插件 cmdk 插件举例分析。

cmdk 搜索那个就是一个纯 react 组件的功能,作为插件的话其实就是想办法把这个插件在 umi 运行的时候就插入到整个 umi 的最外层。

另外就是支持配置一些参数,比如有些用户不想用 cmd + k,想用 cmd + m 来调出弹框。那么就需要配置快捷键的 key。

大致就这两个功能吧。

  1. 将 cmkd 的 react 组件插入到 umi。

  2. 支持配置快捷键

需要用到 API

  1. [api. describe ()](https://umijs.org/docs/api/plugin-api#describe "api. describe ( "api. describe ()")")。用于在插件注册阶段执行,用于描述插件或者插件集的 key、配置信息和启用方式等。在 .umirc 配置快捷键的时候需要用到

  2. [api. onGenerateFiles ()](https://umijs.org/docs/api/plugin-api#ongeneratefiles "api. onGenerateFiles ( "api. onGenerateFiles ()")")。生成临时文件的钩子,就是把运行时需要的文件生成到 .umi 目录下的那个钩子。

  3. [api. writeTmpFile ()](https://umijs.org/docs/api/plugin-api#writetmpfile "api. writeTmpFile ( "api. writeTmpFile ()")").生成临时文件的方法,在 onGenerateFiles 阶段进行调用的,将临时文件写入进去。3137dc277a5bc55175643c2dc47dd019.png

看到这些文件,这些就是在 umi 运行时需要用到的文件,都是对应的插件生成的。umi 在运行时这些代码都是会执行的,在我们这里也是需要把 cmdk 那个 react 组件写到这里面来,这样在运行时才能用。

1. 给插件传递属性并做参数校验

比如我们需要将 .umirc 的快捷键配置传递给插件,配置文件大致是这样配置:

export default defineConfig({
  plugins: [join(__dirname, '../src/index.ts')],
  cmdk: {
    keyFilter: "cmd+w",
  },
}

意思就是插件的 key 叫 cmdk,有一个 keyFilter 的配置。插件里的代码就可以这样的写

import type { IApi } from 'umi';

export default (api: IApi) => {
 api.describe({
    key: 'cmdk',  // 定义插件名称,跟 .umirc 的配置的 key 相同。
    config: {
      schema(joi) {  // 返回值,定义配置的 schema 结构,我们只需要这个对象里有一个 keyFilter 的字符串。
        return joi.object({
          keyFilter: joi.string(),
        });
      },
    },
    enableBy: api.EnableBy.config,
  })
}

这样就可以实现对配置参数的校验了。我们可以通过 api.userConfig.cmdk 拿到配置。

2. 给 react 组件传递属性

可以通过 api.userConfig.cmdk 拿到了配置参数,最终目的是要传递给 react 组件的。

src 下新建一个文件 cmdk.tpl 用于方 cmdk 的 react 代码。

然后通过 [api. writeTmpFile ()](https://umijs.org/docs/api/plugin-api#writetmpfile "api. writeTmpFile ( "api. writeTmpFile ()")") 将配置参数传进去,在 umi 插件里由于不是 react 组件,没办法通过 props 传递属性,所以只能通过用模板的方式进行传递参数。

umi 插件用的模板引擎是 Mustache 。

所以插件代码这里就可以这么写:

import type { IApi } from 'umi';

export default (api: IApi) => {
  // 其他代码
 api.onGenerateFiles({
  fn() {
    const runtimeTpl = readFileSync(
   join(__dirname, 'cmdk.tpl'),
   'utf-8',
    );

    api.writeTmpFile({
   path: 'runtime.tsx',
   content: Mustache.render(runtimeTpl, {
     props: JSON.stringify(api.userConfig.cmdk)
   }),
    });
 }
  })
}

这段代码的意思就是在 onGenerateFiles 钩子里,就是生成临时文件的钩子。

先通过 readFileSync 从插件的源码里读取要写入临时文件的模板文件 cmkd.tpl,然后再通过 writeTmpFile 写入到 .umi 下去,同时将 api.userConfig.cmdk 作为参数,写入到 props 这个模板参数里.

umi 会自动写到对应的插件目录下。

cmdk.tpl 里可以这样拿到传递的参数。

const _props =  {{{ props }}};
const { keyFilter = 'meta.k' } =  _props;

第一行是 Mustache 模板引擎的语法,用于变量替换,第二行就是简单的一个解构。这样就可以拿到 keyFilter 了。

大致思路就是这样了。

另外--如何拿到整个 rootContainer

因为我们需要把我们的 react 组件,放到组件的最外层,可以通过 rootContainer 这个函数进行处理。代码如下:

export function rootContainer(container) {
  return <>
    {container}
    <CommandMenu></CommandMenu>
  </>
}

CommandMenu 就是写的 React 组件,跟 container 平级放就好了,container 就是整个 react 容器。

到此,基本功能就可以了,接下来就是发布了。

发布

打包构建

由于该例子是基于 umi 脚手架创建的,本身集成了 umi,打包什么的都很方便。

只需要关注 2 个点:

  1. .fatherrc.ts 的配置是否正确:

import { defineConfig } from 'father';

export default defineConfig({
  cjs: { output: 'dist' },
  esm: { output: 'es' },
});

主要关注需要打包的模块化文件,cjs 是 commonjs 的,如果需要在 node 环境,就需要配置一下,如果只是浏览器环境用的包,只需要配置 esm 即可。

  1. 需要关注一下 package.json 里的入口文件是否跟输出的文件匹配:00fb3421a2663554a7c789d569671637.pngmodule 填的是 es module 的入口文件。

最后再执行一下 npm run build ,即可将产物构建出来。

发布到 npm

产物构建出来之后,然后再执行:

npm publish

将模块发布到 npm 上供别人使用。

小结

本文介绍了如何编写 umi 插件,包括插件的结构和编写过程。通过编写 umi 插件,你可以扩展 umi 的功能,提高开发效率,并且可以将你的插件分享给其他人使用。

当你开始编写 umi 插件时,建议你先了解 umi 的基本用法和原理,并且熟悉 React、Webpack 和 Node. js 等相关技术。同时,你也可以参考 umi 官方提供的插件列表和开源社区中的插件,以了解其他人是如何编写 umi 插件的,并从中学习到一些有用的技巧和经验。

希望本文能对你有所帮助,祝你编写出高质量的 umi 插件!

参考文章

  • [插件开发 (umijs.org)](https://v3.umijs.org/zh-CN/guide/plugin-develop "插件开发 (umijs.org "插件开发 (umijs.org)")")

参考资料

[1]

plugin-umi-cmdk: https://github.com/crazylxr/plugin-umi-cmdk

[2]

开发插件 | UmiJS: https://umijs.org/docs/guides/plugins

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/490440.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络】面试高频问题汇总及详细解答

【C语言部分】面试高频问题汇总及详细解答 【操作系统(Linux)】面试高频问题汇总及详细解答 【数据库】面试高频问题汇总及详细解答 本文目录 1. 简述网络七层参考模型及每一层的作用2. 简述静态路由和动态路由3. 说说有哪些路由协议&#xff0c;都是如何更新的4. 简述域名解析…

PostgreSQL Explain 复杂执行计划怎么看 --- 逐个分解PG执行计划的那些操作

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Codeforces-Round-805-Div-3-E-Split-Into-Two-Sets

title: Codeforces Round 805 (Div. 3) E. Split Into Two Sets date: 2023-04-25 18:14:41 categories: AlgorithmCodeforces tags:codeforces并查集1600 E. Split Into Two Sets 题目大意 给你n组数&#xff0c;每组里面有两个数字&#xff0c;问你能不能把这n组数分为两组…

【数据架构系列-06】一文搞懂数据模型的3种类型——概念模型、逻辑模型、物理模型

数据模型就是模拟现实世界的方法论&#xff0c;是通向智慧世界的基石&#xff01; 从现实世界发展到智慧世界&#xff0c;要数经历现实世界、信息世界、计算机世界、数据世界、智慧世界五个不同的世界&#xff0c;我们天生具有从混沌的世界抽象信息变为信息世界的能力&#xff…

基于jenkinsfile布置java工程

需求 通过jenkins发布java项目到服务器 预备环境 项目地址&#xff1a; https://gitee.com/asaland/sb-docker-appJenkins 2.387.3 通过Jenkinsfile实现方式 jenkins ui 配置pipeline 什么是pipeline? 直接看注释吧&#xff0c;简单点就是编排可以多个跨时间的构建代理…

JavaScript全解析——canvas 绘制变换和渐变

绘制变换 ●在 cancas 内, 也可以向 css 中一样, 出现一些 2d 变换的效果 ●先来绘制一个基本矩形 // 0. 获取到页面上的 canvas 标签元素节点 const canvasEle document.querySelector(#canvas)// 1. 获取当前这个画布的工具箱 const ctx canvasEle.getContext(2d)// 2. 绘…

ip数据报计算首部检验和

当我们在使用互联网进行数据传输时&#xff0c;数据可能会被篡改或者损坏。为了保证数据传输的可靠性和完整性&#xff0c;计算IP数据包首部检验和是一种非常重要的校验机制。本文将会介绍计算IP数据包首部检验和的方法。 IP数据包首部 首先&#xff0c;我们需要了解IP数据包…

5 系统数据文件和信息

5.1 口令文件 口令文件包含了下表中所示的各字段&#xff0c;这些字段包含在<pwd.h>中定义的passwd结构中。 /etc/passwd文件中的字段 说 明struct passwd 成员用户名char *pw_name加密口令char *pw_passwd数值用户IDuid_t pw_uid数值组IDgid_t pw_gid注释字段char *pw_g…

Unity实现GPU Cull渲染

前言 开放世界游戏中植被和物件的数量往往是巨大, 而传统组织大量植被渲染的方式是利用QuadTree/Octree/Kd-Tree等数据结构对植被Intance数据进行预先生成一个个Cluster&#xff0c;然后在运行时进行FrustumCull,收集可视的所有Cluster&#xff0c;最后进行DrawInstance. 这…

Yolov1 源码讲解 loss.py

结构 1.lt rb我觉得不是很合适 正确来说是lb rt 因为比较出来的都是左下和右上坐标 比如前两个&#xff0c;都是max出来的 选两个box左下坐标中最大的&#xff0c; 后两个则是右上坐标中最小的 那也就形成了交集面积 但是代码中仍然是lt rb我也就直接这样说 而算出lt和r…

zynq基于XDMA实现PCIE X8视频采集HDMI输出 提供工程源码和QT上位机程序和技术支持

目录 1、前言2、我已有的PCIE方案3、基于zynq架构的PCIE4、总体设计思路和方案视频输入通路PCIE数据缓存通路视频输出通路 5、vivado工程详解6、SDK 工程详解7、驱动安装8、QT上位机软件9、上板调试验证10、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Expre…

二叉树的实现

二叉树 文章目录 二叉树背景二叉树的概念遍历方式代码实现 背景 数组存储方式的分析 优点&#xff1a;通过下标方式访问元素&#xff0c;速度快。对于有序数组&#xff0c;还可使用二分查找提高检索速度。 缺点&#xff1a;如果要检索具体某个值&#xff0c;或者插入值(按一…

linux中使用docker部署微服务

目录 一、制作jar包&#xff08;如果看一眼很简单&#xff0c;可以直接使用结尾的jar&#xff09; 1.首先创建一个微服务 demo2 2.启动微服务&#xff08;在DemoApplication上右键执行启动就行&#xff09; 注意&#xff1a;其他操作导致的 可能遇到的报错 3.修改端口 4.新…

ChatGPT的快速发展究竟给我们带来了什么?

&#x1f61a;一个不甘平凡的普通人&#xff0c;致力于为Golang社区和算法学习做出贡献&#xff0c;期待您的关注和认可&#xff0c;陪您一起学习打卡&#xff01;&#xff01;&#xff01;&#x1f618;&#x1f618;&#x1f618; &#x1f917;专栏&#xff1a;算法学习 &am…

java基础入门-05-【面向对象进阶(static继承)】

Java基础入门-05-【面向对象进阶&#xff08;static&继承&#xff09;】 13、面向对象进阶&#xff08;static&继承&#xff09;1.1 如何定义类1.2 如何通过类创建对象1.3 封装1.3.1 封装的步骤1.3.2 封装的步骤实现 1.4 构造方法1.4.1 构造方法的作用1.4.2 构造方法的…

Unity API详解——Random类

Random类是Unity中用于产生随机数的类&#xff0c;不可以实例化&#xff0c;只有静态属性和静态方法。本博客主要介绍了Random类的一些静态属性。 文章目录 一、Random类静态属性1、基本语法2、功能说明3、代码实现 二、rotationUniform属性1、基本语法2、功能说明1、规范化向量…

前沿探索,AI 在 API 开发测试中的应用

目录 一、引言二、AI 加持下的 API 设计1、NLP 在 API 设计中的应用2、DL 在 API 设计中的应用能力一&#xff1a;Apikit 如何利用 AI 生成最佳的 API 设计方案能力二&#xff1a; Apikit 如何利用 AI 提高 API 的可用性和易用性 三、AI 加持下的 API 开发能力三&#xff1a;Ap…

k8s二进制安装部署(详细)(3主2从)

目录 kubeadm 和二进制安装 k8s 适用场景分析 多 master 节点高可用架构图 集群环境准备 部署过程 修改主机内核参数&#xff08;所有节点&#xff09; 配置阿里云的repo源&#xff08;所有节点&#xff09; 配置国内安装 docker 和 containerd 的阿里云的 repo 源 配置…

比肩 ChatGPT,国内快速访问的强大 AI 工具 Claude

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;蚂蚁集团高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐…

Gateway案例

官网:Spring Cloud Gateway 中文文档:Spring Cloud Gateway 2.1.0 中文官网文档 - 腾讯云开发者社区-腾讯云 一、网关介绍: 网关就是当前微服务的统一入口 通常在微服务项目中,只有网关项目是暴露在网络里的,其他服务一般都是在内网里, 用户访问网关,网关根据访问的路径,来进…