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

beforeInitContainer

AsyncWaterfallHook

在 host(消费者)调用 remoteEntry.init(...) 之前触发,可用于动态改写本次初始化使用的 shareScope / initScope,以及传给 remote 的 remoteEntryInitOptions

  • type
async function beforeInitContainer(args: BeforeInitContainerOptions): Promise<BeforeInitContainerOptions>

type BeforeInitContainerOptions ={
  shareScope: ShareScopeMap[string];
  initScope: InitScope;
  remoteEntryInitOptions: RemoteEntryInitOptions;
  remoteInfo: RemoteInfo;
  origin: ModuleFederation;
}

initContainer

AsyncWaterfallHook

remoteEntry.init(...) 调用成功后触发。可用于在容器初始化完成后执行观测、埋点或补充处理。

  • type
async function initContainer(args: InitContainerOptions): Promise<InitContainerOptions>

type InitContainerOptions ={
  shareScope: ShareScopeMap[string];
  initScope: InitScope;
  remoteEntryInitOptions: RemoteEntryInitOptions;
  remoteInfo: RemoteInfo;
  remoteEntryExports: RemoteEntryExports;
  origin: ModuleFederation;
  id?: string;
  remoteSnapshot?: ModuleInfo;
}

handlePreloadModule

SyncHook

处理 remotes 的预加载逻辑。

  • type
function handlePreloadModule(args: HandlePreloadModuleOptions): void

type HandlePreloadModuleOptions ={
  id: string;
  name: string;
  remote: Remote;
  remoteSnapshot: ModuleInfo;
  preloadConfig: PreloadRemoteArgs;
  origin: ModuleFederation;
}

errorLoadRemote

AsyncHook

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

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

type ErrorLoadRemoteOptions ={
  id: string;
  error: unknown;
  options?: any;
  from: 'build' | 'runtime';
  lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
  origin: ModuleFederation;
}

lifecycle 表示错误发生阶段:

  • beforeRequest: 处理 remote 请求参数阶段出错
  • afterResolve: 解析/拉取 manifest 阶段出错(常见于网络异常)
  • onLoad: 加载 exposes 模块阶段出错
  • beforeLoadShare: 加载 shared 依赖阶段出错
  • 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;
}

initContainerShareScopeMap

SyncWaterfallHook

在 host(消费者)初始化 remote(生产者)共享池映射时触发,可用于对齐/重定向某个 scope 的共享池对象(例如把 scope1 直接指向 default,让两者共用同一套共享依赖池)。

  • type
function initContainerShareScopeMap(args: InitContainerShareScopeMapOptions): InitContainerShareScopeMapOptions

type InitContainerShareScopeMapOptions ={
  shareScope: ShareScopeMap[string];
  options: Options;
  origin: ModuleFederation;
  scopeName: string;
  hostShareScopeMap?: ShareScopeMap;
}

resolveShare

SyncWaterfallHook

允许改写最终选中的共享模块结果。

resolveShare 触发时,运行时已经先选好了候选的 scope 和 version。这个阶段单独修改 args.scopeargs.version 之类的字段,并不会自动影响最终结果。要真正改掉最终使用的 shared,需要改写 args.resolver,让它返回你希望使用的那条记录。

  • type
function resolveShare(args: ResolveShareOptions): ResolveShareOptions

type ResolveShareOptions ={
  shareScopeMap: ShareScopeMap;
  scope: string;
  pkgName: string;
  version: string;
  shareInfo: Shared;
  GlobalFederation: Federation;
  resolver: () => {
    shared: Shared;
    useTreesShaking: boolean;
  } | 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 { pkgName, shareScopeMap } = args;

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

        const fallbackShared = shareScopeMap.default?.react?.['17.0.0'];
        if (!fallbackShared) {
          return args;
        }

        args.resolver = function () {
          return {
            shared: fallbackShared,
            useTreesShaking: false,
          };
        };
        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()]
});

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

beforePreloadRemote

AsyncHook

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

  • type
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): Promise<void>

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

loaderHook 用于拦截资源加载与工厂获取流程。

createScript

SyncHook

用于修改加载资源时的 script

  • type
function createScript(args: CreateScriptOptions): CreateScriptHookReturn

type CreateScriptOptions ={
  url: string;
  attrs?: Record<string, any>;
}
  • 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'
      });
    },
  }
};

SyncHook

用于修改预加载/样式加载时创建的 link 元素。

  • type
function createLink(args: CreateLinkOptions): HTMLLinkElement | void

type CreateLinkOptions ={
  url: string;
  attrs?: Record<string, any>;
}

loadEntryError

AsyncHook

在 remoteEntry 加载失败(通常是脚本加载异常)时触发,可用于重试或自定义兜底。

  • type
async function loadEntryError(args: LoadEntryErrorOptions): Promise<Promise<RemoteEntryExports | undefined> | undefined>

type LoadEntryErrorOptions ={
  getRemoteEntry: typeof getRemoteEntry;
  origin: ModuleFederation;
  remoteInfo: RemoteInfo;
  remoteEntryExports?: RemoteEntryExports;
  globalLoading: Record<string, Promise<void | RemoteEntryExports> | undefined>;
  uniqueKey: string;
}

getModuleFactory

AsyncHook

在调用 remoteEntry.get(expose) 前触发,可自定义模块工厂获取逻辑。

  • type
async function getModuleFactory(args: GetModuleFactoryOptions): Promise<(() => Promise<Module>) | undefined>

type GetModuleFactoryOptions ={
  remoteEntryExports: RemoteEntryExports;
  expose: string;
  moduleInfo: RemoteInfo;
}

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"

bridgeHook

bridgeHook 定义在 runtime-core/src/core.ts,用于桥接渲染/销毁阶段(如 React/Vue bridge)扩展上下文。

beforeBridgeRender

SyncHook

在桥接渲染前触发,可返回对象扩展渲染参数(例如追加 extraProps)。

  • type
function beforeBridgeRender(args: Record<string, any>): void | Record<string, any>

afterBridgeRender

SyncHook

在桥接渲染后触发。

  • type
function afterBridgeRender(args: Record<string, any>): void | Record<string, any>

beforeBridgeDestroy

SyncHook

在桥接销毁前触发。

  • type
function beforeBridgeDestroy(args: Record<string, any>): void | Record<string, any>

afterBridgeDestroy

SyncHook

在桥接销毁后触发。

  • type
function afterBridgeDestroy(args: Record<string, any>): void | Record<string, any>