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 中文文档

相关推荐
CCF_NOI.10 分钟前
谷歌浏览器深入用法全解析:解锁高效网络之旅
大数据·运维·服务器·前端·计算机·谷歌
paopaokaka_luck3 小时前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
患得患失9494 小时前
【前端】【vscode】【.vscode/settings.json】为单个项目配置自动格式化和开发环境
前端·vscode·json
飛_4 小时前
解决VSCode无法加载Json架构问题
java·服务器·前端
YGY Webgis糕手之路6 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
90后的晨仔7 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
Ares-Wang7 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
90后的晨仔7 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
德育处主任7 小时前
p5.js 正方形square的基础用法
前端·数据可视化·canvas