Runtime 接入

Tip

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

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

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

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

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


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

运行时注册模块插件中注册模块
可脱离构建插件使用,在 webpack4 等项目中可直接使用纯运行时进行模块注册和加载构建插件需要是 webpack5 或以上
支持动态注册模块不支持动态注册模块
不支持 import 语法加载模块支持 import 同步语法加载模块
支持 loadRemote 加载模块支持 loadRemote 加载模块
设置 shared 必须提供具体版本和实例信息设置 shared 只需要配置规则即可,无须提供具体版本及实例信息
shared 依赖只能供外部使用,无法使用外部 shared 依赖shared 依赖按照特定规则双向共享
可以通过 runtimeplugin 机制影响加载流程目前不支持提供 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,运行时应该从 @module-federation/modern-js/runtime 中导出运行时 API。这样能保证插件和运行时使用的是同一个运行时实例,保证模块加载正常

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

模块注册

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);
});