Runtime 接入

Tip

在阅读本章前,预设你已经了解:

目前 Module Federation 提供了两种注册模块和加载模块的方式:

  • 一种是在构建插件中声明(一般是在 module-federation.config.ts 文件中声明)

  • 另一种方式是直接通过 runtime 的 api 进行模块注册和加载。

两种模式并不冲突可结合使用。你可以根据你的实际场景灵活选取模块注册方式和时机


运行时注册模块和构建配置注册模块的区别如下:

运行时注册模块插件中注册模块
可脱离构建插件使用,在 webpack4 等项目中可直接使用纯运行时进行模块注册和加载构建插件需要是 webpack5 或以上
支持动态注册模块不支持动态注册模块
不支持 import 语法加载模块支持 import 同步语法加载模块
支持 loadRemote 加载模块支持 loadRemote 加载模块
设置 shared 必须提供具体版本和实例信息设置 shared 只需要配置规则即可,无须提供具体版本及实例信息
shared 依赖只能供外部使用,无法使用外部 shared 依赖shared 依赖按照特定规则双向共享
可以通过 runtime 的 plugin 机制影响加载流程目前不支持提供 plugin 影响加载流程
不支持远程类型提示支持远程类型提示

若使用构建插件,项目启动时将自动创建 ModuleFederation 实例并存储于内存中。此时可直接调用 API,API 会自动从内存中获取构建运行时创建的 ModuleFederation 实例。

import { loadRemote } from '@module-federation/enhanced/runtime';

loadRemote('remote1');

若未使用构建插件,则需手动创建 ModuleFederation 实例,之后调用相应 API。

import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'host',
  remotes: [
    {
      name: 'remote1',
      entry: 'http://localhost:2001/vmok-manifest.json',
    },
  ],
});

mf.loadRemote('remote1');
  • 什么是 ModuleFederation 实例 ?

ModuleFederation 实例是 ModuleFederation 类的实例,它包含了 ModuleFederation 运行时的所有功能。

你可以在控制台输入 __FEDERATION__.__INSTANCES__ 来查看已经创建好的实例。

安装

npm
yarn
pnpm
bun
npm add @module-federation/enhanced --save
注意:
  • 以下 Federation Runtime 示例我们均展示脱离特定框架如 Modern.js 的 case, 所以 API 将均从初始 @module-federation/enhanced/runtime 包中导出。

  • 如果你的项目是 Modern.js 项目且使用 @module-federation/modern-js-v3,运行时应该从 @module-federation/modern-js-v3/runtime 中导出运行时 API。这样能保证插件和运行时使用的是同一个运行时实例,保证模块加载正常

  • 如果你的项目是 Modern.js 项目但是没有使用 @module-federation/modern-js-v3,则应当从 @module-federation/enhanced/runtime 导出 runtime API。但是我们推荐你使用 @module-federation/modern-js-v3 进行模块注册和加载,这将使你享受到更多和框架结合的能力。

模块注册

Build Plugin(使用构建插件)
Pure Runtime(未使用构建插件)
// 如果使用了构建插件,那么可以直接使用 `registerRemotes` 注册模块。
import { registerRemotes } from '@module-federation/enhanced/runtime';

registerRemotes([
  {
      name: 'remote1',
      alias: 'remote-1',
      entry: 'http://localhost:3001/mf-manifest.json',
  }
]);

模块加载

Build Plugin(使用构建插件)
Pure Runtime(未使用构建插件)
// 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。
import { loadRemote } from '@module-federation/enhanced/runtime';
import React from 'react';

export default () => {
  const MyButton = React.lazy(() =>
    loadRemote('remote1').then(({ MyButton }) => {
      return {
        default: MyButton
      };
    }),
  );

  return (
    <React.Suspense fallback="Loading Button">
      <MyButton />
    </React.Suspense>
  );
}

加载匿名模块

Build Plugin(使用构建插件)
Pure Runtime(未使用构建插件)
// 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。
import React from 'react';
import { loadRemote } from '@module-federation/enhanced/runtime';

const RemoteButton = React.lazy(() => loadRemote('provider/button'));
// 也可通过模块别名加载:
// const RemoteButton = React.lazy(() => loadRemote('remotes-1/button'));

export default () => {
  return (
    <React.Suspense fallback="Loading Button">
      <RemoteButton />
    </React.Suspense>
  );
}

加载具名模块

Build Plugin(使用构建插件)
Pure Runtime(未使用构建插件)
// 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。
import React from 'react';
import { loadRemote } from '@module-federation/enhanced/runtime';

export default () => {
  const RemoteButton = React.lazy(() =>
    loadRemote('remote1/button').then(({ RemoteButton }) => {
      return {
        default: RemoteButton
      };
    }),
  );
  return (
    <React.Suspense fallback="Loading Button">
      <RemoteButton />
    </React.Suspense>
  );
}

加载工具函数

Build Plugin(使用构建插件)
Pure Runtime(未使用构建插件)
// 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。
import React from 'react';
import { loadRemote } from '@module-federation/enhanced/runtime';

// 加载 remote1 expose 的 util
loadRemote<{add: (...args: Array<number>)=> number }>("remote1/util").then((md)=>{
    md.add(1,2);
});