logologo
指南
实践
配置
插件
案例
博客
生态
Module Federation Examples
Practical Module Federation
Zephyr Cloud
Nx
简体中文
English
指南
实践
配置
插件
案例
博客
Module Federation Examples
Practical Module Federation
Zephyr Cloud
Nx
简体中文
English
logologo

开始

介绍
设置环境
快速上手
功能导航
名词解释
npm 包

基础

运行时

Runtime 接入
Runtime API
Runtime Hooks
Rsbuild Plugin
Rspack 插件
Webpack Plugin
Rspress Plugin
Vite Plugin
Metro
类型提示
命令行工具
样式隔离

数据管理

数据获取
数据缓存
Prefetch

框架

Modern.js
Next.js

部署

使用 Zephyr Cloud 部署

调试

开启调试模式
Chrome Devtool
全局变量

Troubleshooting

概览

运行时

RUNTIME-001
RUNTIME-002
RUNTIME-003
RUNTIME-004
RUNTIME-005
RUNTIME-006
RUNTIME-007
RUNTIME-008
RUNTIME-009

构建

BUILD-001
BUILD-002

类型

概览
TYPE-001
其他
Edit this page on GitHub
Previous PageRuntime API
Next PageRsbuild Plugin

#Runtime Hooks

#beforeInit

SyncWaterfallHook

在 MF 实例初始化之前更新对应 init 配置

  • type
function beforeInit(args: BeforeInitOptions): BeforeInitOptions

type BeforeInitOptions ={
    userOptions: UserOptions;
    options: ModuleFederationRuntimeOptions;
    origin: ModuleFederation;
    shareInfo: ShareInfos;
}

interface ModuleFederationRuntimeOptions {
  id?: string;
  name: string;
  version?: string;
  remotes: Array<Remote>;
  shared: ShareInfos;
  plugins: Array<ModuleFederationRuntimePlugin>;
  inBrowser: boolean;
}

#init

SyncHook

在 MF 实例初始化后调用

  • type
function init(args: InitOptions): void

type InitOptions ={
  options: ModuleFederationRuntimeOptions;
  origin: ModuleFederation;
}

#beforeRequest

AsyncWaterfallHook

在解析 remote 路径前调用,对于在查找之前更新某些内容很有用。

  • type
async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>

type BeforeRequestOptions ={
  id: string;
  options: ModuleFederationRuntimeOptions;
  origin: ModuleFederation;
}

#afterResolve

AsyncWaterfallHook

在解析 remote 路径后调用,允许修改解析后的内容。

  • type
async function afterResolve(args: AfterResolveOptions): Promise<AfterResolveOptions>

type AfterResolveOptions ={
  id: string;
  pkgNameOrAlias: string;
  expose: string;
  remote: Remote;
  options: ModuleFederationRuntimeOptions;
  origin: ModuleFederation;
  remoteInfo: RemoteInfo;
  remoteSnapshot?: ModuleInfo;
}

#onLoad

AsyncHook

Triggered once a federated module is loaded, allowing access and modification to the exports of the loaded file.

加载 remote 后触发,允许访问和修改已加载文件的导出(exposes)。

  • type
async function onLoad(args: OnLoadOptions): Promise<void>

type OnLoadOptions ={
  id: string;
  expose: string;
  pkgNameOrAlias: string;
  remote: Remote;
  options: ModuleOptions;
  origin: ModuleFederation;
  exposeModule: any;
  exposeModuleFactory: any;
  moduleInstance: Module;
}

type ModuleOptions = {
    remoteInfo: RemoteInfo;
    host: ModuleFederation;
}

interface RemoteInfo {
  name: string;
  version?: string;
  buildVersion?: string;
  entry: string;
  type: RemoteEntryType;
  entryGlobalName: string;
  shareScope: string;
}

#handlePreloadModule

SyncHook

处理 remotes 的预加载逻辑。

  • type
function handlePreloadModule(args: HandlePreloadModuleOptions): void

type HandlePreloadModuleOptions ={
  id: string;
  name: string;
  remoteSnapshot: ModuleInfo;
  preloadConfig: PreloadRemoteArgs;
}

#errorLoadRemote

AsyncHook

如果加载 remotes 失败,则调用,从而启用自定义错误处理。可返回自定义的兜底逻辑。

  • type
async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>

type ErrorLoadRemoteOptions ={
  id: string;
  error: unknown;
  from: 'build' | 'runtime';
  origin: ModuleFederation;
}
  • example
import { createInstance } from '@module-federation/enhanced/runtime'

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

const fallbackPlugin: () => ModuleFederationRuntimePlugin =
  function () {
    return {
      name: 'fallback-plugin',
      errorLoadRemote(args) {
        const fallback = 'fallback'
        return fallback;
      },
    };
  };


const mf = createInstance({
    name: 'mf_host',
    remotes: [
        {
            name: "remote",
            alias: "app1",
            entry: "http://localhost:2001/mf-manifest.json"
        }
    ],
    plugins: [fallbackPlugin()]
});

mf.loadRemote('app1/un-existed-module').then(mod=>{
  expect(mod).toEqual('fallback');
})

#beforeLoadShare

AsyncWaterfallHook

在加载 shared 之前调用,可用于修改对应的 shared 配置

  • type
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>

type BeforeLoadShareOptions ={
  pkgName: string;
  shareInfo?: Shared;
  shared: Options['shared'];
  origin: ModuleFederation;
}

#resolveShare

SyncWaterfallHook

允许手动设置实际使用的共享模块。

  • type
function resolveShare(args: ResolveShareOptions): ResolveShareOptions

type ResolveShareOptions ={
  shareScopeMap: ShareScopeMap;
  scope: string;
  pkgName: string;
  version: string;
  GlobalFederation: Federation;
  resolver: () => Shared | undefined;
}
  • example
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'

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

const customSharedPlugin: () => ModuleFederationRuntimePlugin =
  function () {
    return {
      name: 'custom-shared-plugin',
      resolveShare(args) {
        const { shareScopeMap, scope, pkgName, version, GlobalFederation } = args;

        if (
          pkgName !== 'react'
        ) {
          return args;
        }

        args.resolver = function () {
          shareScopeMap[scope][pkgName][version] = window.React; // replace local share scope manually with desired module
          return shareScopeMap[scope][pkgName][version];
        };
        return args;
      },
    };
  };


const mf = createInstance({
    name: 'mf_host',
    shared: {
      react: {
        version: '17.0.0',
        scope: 'default',
        lib: () => React,
        shareConfig: {
          singleton: true,
          requiredVersion: '^17.0.0',
        },
      },
    },
    plugins: [customSharedPlugin()]
});

window.React = ()=> 'Desired Shared';

mf.loadShare("react").then((reactFactory)=>{
  expect(reactFactory()).toEqual(window.React());
});

#beforePreloadRemote

AsyncHook

在预加载处理程序执行任何预加载逻辑之前调用

  • type
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): BeforePreloadRemoteOptions

type BeforePreloadRemoteOptions ={
  preloadOps: Array<PreloadRemoteArgs>;
  options: Options;
  origin: ModuleFederation;
}

#generatePreloadAssets

AsyncHook

用于控制生成需要预加载的资源

  • type
async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise<PreloadAssets>

type GeneratePreloadAssetsOptions ={
  origin: ModuleFederation;
  preloadOptions: PreloadOptions[number];
  remote: Remote;
  remoteInfo: RemoteInfo;
  remoteSnapshot: ModuleInfo;
  globalSnapshot: GlobalModuleInfo;
}

interface PreloadAssets {
  cssAssets: Array<string>;
  jsAssetsWithoutEntry: Array<string>;
  entryAssets: Array<EntryAssets>;
}

#loaderHook

#createScript

SyncHook

用于修改加载资源时的 script

  • type
function createScript(args: CreateScriptOptions): HTMLScriptElement | void

type CreateScriptOptions ={
  url: string;
}
  • example
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
  function () {
    return {
      name: 'change-script-attribute',
      createScript({ url }) {
        if (url === testRemoteEntry) {
          let script = document.createElement('script');
          script.src = testRemoteEntry;
          script.setAttribute('loader-hooks', 'isTrue');
          script.setAttribute('crossorigin', 'anonymous');
          return script;
        }
      }
    };
  };

#fetch

fetch 函数允许自定义获取清单(manifest)JSON 的请求。成功的 Response 必须返回一个有效的 JSON。

AsyncHook

  • Type
function fetch(manifestUrl: string, requestInit: RequestInit): Promise<Response> | void | false;
  • 示例:在获取清单(manifest)JSON 时包含凭证:
// fetch-manifest-with-credentials-plugin.ts
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

export default function (): FederationRuntimePlugin {
  return {
    name: 'fetch-manifest-with-credentials-plugin',
    fetch(manifestUrl, requestInit) {
      return fetch(manifestUrl, {
        ...requestInit,
        credentials: 'include'
      });
    },
  }
};

#loadEntry

loadEntry 函数允许对 remotes 进行完全自定义,从而可以扩展并创建新的 remote 类型。以下两个简单示例分别演示了如何加载 JSON 数据以及模块代理(module delegation)。

asyncHook

  • Type
function loadEntry(args: LoadEntryOptions): RemoteEntryExports | void;

type LoadEntryOptions = {
  createScriptHook: SyncHook,
  remoteEntryExports?: RemoteEntryExports,
  remoteInfo: RemoteInfo
};
interface RemoteInfo {
  name: string;
  version?: string;
  buildVersion?: string;
  entry: string;
  type: RemoteEntryType;
  entryGlobalName: string;
  shareScope: string;
}
export type RemoteEntryExports = {
  get: (id: string) => () => Promise<Module>;
  init: (
    shareScope: ShareScopeMap[string],
    initScope?: InitScope,
    remoteEntryInitOPtions?: RemoteEntryInitOptions,
  ) => void | Promise<void>;
};
  • 示例:加载 JSON 数据
// load-json-data-plugin.ts
import { init } from '@module-federation/enhanced/runtime';
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
  return {
    name: 'load-json-data-plugin',
    loadEntry({ remoteInfo }) {
      if (remoteInfo.jsonA === "jsonA") {
        return {
          init(shareScope, initScope, remoteEntryInitOPtions) {},
          async get(path) {
            const json = await fetch(remoteInfo.entry + ".json").then(res => res.json())
            return () => ({
              path,
              json
            })
          }
        }
      }
    },
  };
};
// module-federation-config
{
  remotes: {
    jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package"
  }
}
// src/bootstrap.js
import jsonA from "jsonA"
jsonA // {...json data}
  • 示例:模块代理(Delegate Modules)
// delegate-modules-plugin.ts
import { init } from '@module-federation/enhanced/runtime';
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
  return {
    name: 'delegate-modules-plugin',
    loadEntry({ remoteInfo }) {
      if (remoteInfo.name === "delegateModulesA") {
        return {
          init(shareScope, initScope, remoteEntryInitOPtions) {},
          async get(path) {
            path = path.replace("./", "")
            const {[path]: factory} = await import("./delegateModulesA.js")
            const result = await factory()
            return () => result
          }
        }
      }
    },
  };
};
// ./src/delegateModulesA.js
export async function test1() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("test1 value")
    }, 3000)
  })
}
export async function test2() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("test2 value")
    }, 3000)
  })
}
// module-federation-config
{
  remotes: {
    delegateModulesA: "delegateModulesA@https://delegateModulesA.js"
  }
}
// src/bootstrap.js
import test1 from "delegateModulesA/test1"
import test2 from "delegateModulesA/test2"
test1 // "test1 value"
test2 // "test2 value"