Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

  • Webpack

  • Redux

  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

    • UMI插件-使用插件实现模块与模块的传递
      • 0.前言
      • 1.通用功能 api 加载方式
      • 2. umi4 升级报错及解决方案
      • 3. Umi 中的插件机制
      • 4.实际解决方案
      • 5. 扩展性考虑
    • UMI插件-注入静态信息
    • package.json 中的 main、types、module、exports 字段
  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • UMI插件
wangjiasheng
2023-01-05
目录

UMI插件-使用插件实现模块与模块的传递

# 0.前言

在 umi3 升级 umi4 的过程中遇到一个网络请求相关的问题。在开发通用功能页面时,经常会出现不同模块间组件相互引用的情况。

# 1.通用功能 api 加载方式

每一个通用功能模块下都有一个 apis 文件夹用于存放 接口文件,配置字段如下:

// apis.ts 文件
export default {
  alias: {
    name: "xxxxxx",
    code: "",
    method: "",
    url: () => "/api/unit/list",
  },
};
1
2
3
4
5
6
7
8
9

在通用模块的入口文件中,会将此 apis.ts 注册到 @xxx/request 依赖中,代码如下:

import { addApis } from "@xxx/request";
import apis from "./apis";
addApis(apis);
1
2
3

由上可见,api 网络请求模块的加载时机是:当前模块被加载后,才会将 apis 文件注入到 @xxx/request 的 api 变量中。

# 2. umi4 升级报错及解决方案

这套执行机制在 umi3 中运行良好,升级到 umi4 后在某一场景下出现问题:

  • 当通用功能模块不使用其余模块页面时,运行正常。
  • 当通用功能模块调用其余模块,且其余模块此前未被点击时,会出现 api 报错。

猜测原因:umi4 升级后,umi 插件采用按需运行的方式执行,只有当模块加载后,被加载模块的 api 接口才可以使用。

解决方案:利用 umi 插件机制在 @xxx/pages 中提前收集所有模块的 api 文件,并在 @xxx/platform 中插件中编译生成 requestData.ts 依赖文件,再通过 createRequest 传递给 @xxx/request 模块。

# 3. Umi 中的插件机制

如何将 xxx-pages 工程下收集的内容传递给 xxx-core 工程?

可通过 umi 提供的 hook 钩子函数实现(底层采用的是和 webpack 相同的 tapable 库)

在 umi 中提供两种 hook 函数注册方式:register 和 registerMethods 函数。

umi 新官网这块写的不错,后续补充进去:https://umijs.org/docs/api/plugin-api#register

# 4.实际解决方案

在 xxx-pages 工程下的 plugin-xxx-pages 插件中主要实现两个功能:

  1. 在 config.ts 中暴露出一个 xxxPages 属性,支持对路由页面的加载控制:

    export default defineConfig({
      xxxPages: {
        excludes: ["xxx1", "xxx2", "xxx3"],
        includes: ["yyy1", "yyy2", "yyy3"],
      },
    });
    
    1
    2
    3
    4
    5
    6
  2. 功能二:收集所有通用功能页面的 apis 文件夹信息。

这里着重介绍功能二的写法:

export default function (api:IApi){
    .........,
    api.register({
      key: "addApi", /* 特别注意:在 umi 中 hook 以 `addXxx` 开头时,默认为 add 模式 */
      fn: ()=>{
        const filterRoutes = includeRoutes(excludeRoutes(customeRoutes));
        const apis = filterRoutes.map((item)=>{
          /* 收集所有 packages 下构建 lib 文件夹下所有的 apis 文件*/
          let api = winPath(join(xxxx,"./lib","apis"));
          /* 检测此文件夹路径是否存在 */
          if(existsSync(api)){
            /* 将 api 文件夹路径返回 */
            return {
              name: item.access,
              path: api
            }
            return false;
          }
        }).filter(Boolean);
        return [
          /* 此文件夹本来用于收集公共 api 文件,现在感觉可以删掉了 */
          {
            name: "customApis",
            path: "@umijs/plugin-xxx-pages/lib/apis"
          },
          ...apis
        ]
      }
    })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

在 xxx-platform 下的 plugin-xxx-request 模块下,会构建生成 requestData 文件

在此 umi 插件中,此功能模块的示例代码如下:

import { winPath, Mustache } from "@umijs/utils"; /* 使用 Mustache 作为模板解析引擎*/
export default function (api){
  api.onGenerateFiles(async ()=>{
    const {paths} = api;

    /* 此函数用于收集 pc-local-demo 下的 apis 文件 */
    function getApiList(){
      /* 可根据配置读 api 文件夹还是 apis 文件夹 */
      const apiFolder = api.config?.singular ? 'api' : 'apis';
      const apiFiles = glob.sync('*.{ts,js,json}',{
        cwd: winPath(join(paths.absSrcPath!, apiFolder))
      }).map(()=>{
        ..... // 此部分对数据做一层 Format 操作,不是很重要
      })
      return apiFiles;
    }

    /*
       api.applyPlugins 触发 `addApi` 这个 Hook 钩子,并是指定一个初始值 getApiList()
       `apiFiles` 在 add 模式下是一个数组,会将 getApiList 的结果作为数组的第一项,
       将 `xxx-pages` 中收集的 apis 数组项 push 到数组后面。
    */
    const apiFiles = await api.applyPlugins({
      key: "addApi",
      type: api.ApplyPluginsType.add /* 也可以显式指明为 add 模式*/,
      initialValue: getApiList();
    })

    api.writeTmpFile({
      path: `requestData.ts`,
      content: Mustache.render(requestDataTpl,{apiFiles,xxxx,xxx})
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

在 umi 插件中,可通过 const { path } = api; 方式暴露出许多实用的地址:

  • path
    • absNodeModulesPath,node_modules 目录绝对路径
    • absOutputPath,输出路径,默认是 ./dist
    • absPagesPath,pages 目录绝对路径
    • absSrcPath,src 目录绝对路径,需注意 src 目录是可选的,如果没有 src 目录,absSrcPath 等同于 cwd。
    • absTmpPath,临时目录绝对路径
    • cwd,当前路径

注意: 注册阶段不能获取到。因此不能在插件里直接获取,要在 hook 里使用。

根据 Mustache 模块解析引擎,解析如下代码:

// .tpl 文件
{{#apiFiles}}
import {{name}} from "{{{path}}}";
{{/apiFiles}}

{{#apiFiles}}
const apis = {
	...defaultApis,
	{{#apiFiles}}
  ...{{name}}
  {{/apiFiles}}
}
{{/apiFiles}}
1
2
3
4
5
6
7
8
9
10
11
12
13

最终结果会在 src/.umi/plugin-xxxRequest 文件夹下生成 requestData.ts 文件

import index from "@/apis/index"; //  此部分为 getApiList() 获取的结果
import customApis from "@umijs/plugin-xxx-pages/lib/apis"; // 此部分为自定义 api 文件
import xx1 from "xxx:xxxxx/@xxx/page-xxx1/lib/apis";
import xx2 from "xxx:xxxxx/@xxx/page-xxx2/lib/apis";
import xx3 from "xxx:xxxxx/@xxx/page-xxx3/lib/apis";
const apis = {
  ...index,
  ...cunstomApis,
  ...xxx1,
  ...xxx2,
  ...xxx3,
};
1
2
3
4
5
6
7
8
9
10
11
12

最后通过此配置文件被 @xxx/request 的一个参数传入创建 requestInstance 对象,最后经过一系列转化,转化为 umi-request(url,options) 中的 url 以及 option 。

# 5. 扩展性考虑

通过上述方案,已实现 api 文件的注册,通用功能模块中的 addApi(apis) 就显的非常冗余了,实则不然,api 文件依赖的收集需要借助xxx-platform 工程下的 plugin-request 插件实现,若有项目组在使用时不想使用团队提供的 xxxRequest 方法,项目组可自行实现 api 文件收集。

编辑 (opens new window)
上次更新: 2023/01/17, 9:01:00
React 组件复用
UMI插件-注入静态信息

← React 组件复用 UMI插件-注入静态信息→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式