umi+模块联邦-配置指南

背景

目前我们将公共组件封装到npm包里,在每个应用使用npm包的方式进行消费

问题

每次更新组件需要发布npm包,本地开发不能实时看到组件更新效果,需要重新安装npm包才能生效。

解决方法

模块联邦可以在多个 webpack 编译产物之间共享模块、依赖、页面甚至应用,通过全局变量的组合,还可以在不同模块之前进行数据的获取,让跨应用间做到模块共享真正的插拔式的便捷使用。比如a应用如果想使用b应用中table的组件,通过模块联邦可以直接在a中进行import('b/table')非常的方便。

线上 Runtime(运行时) 的效果,让代码直接在项目间利用 CDN 直接共享,不再需要本地安装npm 包、构建再发布了。

umi提供了**Module Federation 插件**

怎么做

共享应用

封装公共组件

导出的模块按照约定,将源代码目录下的 exposes 一级子目录名作为导出项,导出文件为该目录下的 index 文件

typescript 复制代码
//   /exposesCounter/index.tsx
import React from 'react';

export default (props: { init?: number }) => {
  const [c, setC] = React.useState(props.init ?? 10);

  return (
    <div>
      <h1> remote Counter</h1>
      <div>
        <button
          data-testid="remote-button"
          onClick={() => {
            setC((c) => c + 1);
          }}
        >
          click to add new
        </button>
      </div>
      <div>
        remote hooks counter
        <span data-testid="remote-counter">{c}</span>
      </div>
    </div>
  );
};

umirc.ts配置

php 复制代码
import { defineConfig } from '@umijs/max';

const shared = {
  react: {
    singleton: true,
    eager: false,
  },
  'react-dom': {
    singleton: true,
    eager: false,
  },
};

const moduleFederationName = 'remoteCounter'; // 模块名称

export default defineConfig({
  mfsu: false,
  mf: {
    name: moduleFederationName,
    shared,
  },
  publicPath: 'http://127.0.0.1:9000/',
});

使用方

  • keyResolver 用于在运行时决定使用 entries 哪个 key; 推荐使用 立即调用函数表达式 的形式,可以在函数中实现较复杂的功能。不支持异步的函数。
  • keyResolver 也可以使用静态的值,配置形式 keyResolver: '"PROD"'

umirc.ts配置

php 复制代码
import { defineConfig } from "@umijs/max";

const shared = {
  react: {
    singleton: true,
    eager: false,
  },
  "react-dom": {
    singleton: true,
    eager: false,
  },
};

export default defineConfig({
  mfsu:false,
  mf: {
    name: "hostUser",
    remotes: [
      {
        name: "remoteCounter",
        entries: {
          DEV: "http://127.0.0.1:9000/remote.js", // 本地 地址
          PROD: "http://127.0.0.1:9000/remote.js",// 线上地址
        },
        keyResolver: `(()=> 'DEV')()`,
      }
    ],
    shared,
  },
});

使用公共组件

javascript 复制代码
import React, { Suspense } from 'react';

const RemoteCounter = React.lazy(() => {
  // @ts-ignore
  return import('remoteCounter/Counter');
});
export default () => {
    <RemoteCounter />
}

配置项说明

(一)webpack ModuleFederationPlugin配置

  1. name 必须,当前应用的名字,全局唯一ID,通过 name/{expose} 的方式使用
  2. library 可选,打包方式,与 name 保持一致即可
  3. filename 可选,打包后的文件名,对应上面的 remoteEntry.js
  4. remotes 可选,表示当前应用是一个 Host,可以引用 Remote 中 expose 的模块
  5. exposes 可选,表示当前应用是一个 Remote,exposes 内的模块可以被其他的 Host 引用,引用方式为 import(name/{expose})
  6. shared 可选,依赖的包(下面包含了 shared 中包含的配置项)
  • 如果配置了这个属性。webpack在加载的时候会先判断本地应用是否存在对应的包,如果不存在,则加载远程应用的依赖包。

  • 以 app2 来说,因为它是一个远程应用,配置了["react", "react-dom"] ,而它被 app1 所消费,所以 webpack 会先查找 app1 是否存在这两个包,如果不存在就使用 app2 自带包。 app1里面同样申明了这两个参数,因为 app1 是本地应用,所以会直接用 app1 的依赖。

  • shared 配置项指示 remote 应用的输出内容和 host 应用可以共用哪些依赖。 shared 要想生效,则 host 应用和 remote 应用的 shared 配置的依赖要一致。

  • import 共享依赖的实际的 package name,如果未指定,默认为用户自定义的共享依赖名,即 react-shared。如果是这样的话,webpack 打包是会抛出异常的,因为实际上并没有 react-shared 这个包。

  • singleton 是否开启单例模式,true 则开启。如何启用单例模式,那么 remote 应用组件和 host 应用共享的依赖只加载一次,且与版本无关。 如果版本不一致,会给出警告。不开启单例模式下,如果 remote 应用和 host 应用共享依赖的版本不一致,remote 应用和 host 应用需要分别各自加载依赖。

  • requiredVersion 指定共享依赖的版本,默认值为当前应用的依赖版本。- 如果 requiredVersion 与实际应用的依赖的版本不一致,会给出警告。

  • strictVersion 是否需要严格的版本控制。单例模式下,如果 strictVersion 与实际应用的依赖的版本不一致,会抛出异常。默认值为 false。

  • shareKey 共享依赖的别名, 默认值值 shared 配置项的 key 值。

  • shareScope 当前共享依赖的作用域名称,默认为 default。

  • eager 共享依赖在打包过程中是否被分离为 async chunk。eager 为 false, 共享依赖被单独分离为 async chunk; eager 为 true, 共享依赖会打包到 main、remoteEntry,不会被分离。默认值为 false,如果设置为 true, 共享依赖其实是没有意义的。

  • shareScope 所用共享依赖的作用域名称,默认为 default。如果 shareScope 和 share"xxx".shareScope 同时存在,share"xxx".shareScope 的优先级更高。

示例代码

gitlab.evaluateai.cn/liangchaofe...

参考

umijs.org/docs/max/mf...

Mf配置项: ModuleFederationPlugin | webpack

mf配置项的一些解读:juejin.cn

mf原理:Module Federation | webpack 中文文档

相关推荐
醉城夜风~32 分钟前
CSS元素显示模式(display)
前端·css
AI大模型-小华1 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子4 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅4 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni4 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学5 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师6 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc886 小时前
微软EDGE浏览器功能学习
前端·学习·edge
Csvn6 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端