Runtime Hooks
Hook 返回值
对于 SyncHook 和 AsyncHook,返回 undefined 表示插件只观察事件,不会清空前一个插件已经返回的结果。后续插件如果返回新的非 undefined 值,仍然可以替换这个结果。
对于 SyncWaterfallHook 和 AsyncWaterfallHook,需要改写参数时返回完整的新 args 对象;返回 undefined 会保留当前 args,并继续传给下一个插件。
beforeInit
SyncWaterfallHook
在 MF 实例初始化之前更新对应 init 配置
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 实例初始化后调用
function init(args: InitOptions): void
type InitOptions ={
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
beforeRequest
AsyncWaterfallHook
在解析 remote 路径前调用,对于在查找之前更新某些内容很有用。
async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>
type BeforeRequestOptions ={
id: string;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
afterMatchRemote
AsyncHook
在 runtime 把一次 loadRemote 请求匹配到具体 remote 之后触发。如果匹配失败,也会带着 error 触发。适合用于诊断、链路追踪,以及判断问题是否发生在 manifest 或 remoteEntry 加载之前。
async function afterMatchRemote(args: AfterMatchRemoteOptions): Promise<void>
type AfterMatchRemoteOptions ={
id: string;
options: ModuleFederationRuntimeOptions;
remote?: Remote;
expose?: string;
remoteInfo?: RemoteInfo;
error?: unknown;
origin: ModuleFederation;
}
afterResolve
AsyncWaterfallHook
在解析 remote 路径后调用,允许修改解析后的内容。
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)。
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;
}
afterLoadRemote
AsyncHook
在一次 loadRemote 请求结束后触发。加载成功、加载失败、以及通过 errorLoadRemote 兜底恢复的场景都会触发。适合记录 remote 加载的最终状态。
成功加载时,onLoad 会先于 afterLoadRemote 触发。如果加载失败后由 errorLoadRemote 返回兜底结果,afterLoadRemote 会带上原始 error 和 recovered: true。
async function afterLoadRemote(args: AfterLoadRemoteOptions): Promise<void>
type AfterLoadRemoteOptions ={
id: string;
expose?: string;
remote?: RemoteInfo;
options?: {
loadFactory?: boolean;
from?: 'build' | 'runtime';
};
error?: unknown;
recovered?: boolean;
origin: ModuleFederation;
}
beforeInitContainer
AsyncWaterfallHook
在 host(消费者)调用 remoteEntry.init(...) 之前触发,可用于动态改写本次初始化使用的 shareScope / initScope,以及传给 remote 的 remoteEntryInitOptions。
async function beforeInitContainer(args: BeforeInitContainerOptions): Promise<BeforeInitContainerOptions>
type BeforeInitContainerOptions ={
shareScope: ShareScopeMap[string];
initScope: InitScope;
remoteEntryInitOptions: RemoteEntryInitOptions;
remoteInfo: RemoteInfo;
origin: ModuleFederation;
}
initContainer
AsyncWaterfallHook
在 remoteEntry.init(...) 调用成功后触发。可用于在容器初始化完成后执行观测、埋点或补充处理。
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 的预加载逻辑。
function handlePreloadModule(args: HandlePreloadModuleOptions): void
type HandlePreloadModuleOptions ={
id: string;
name: string;
remote: Remote;
remoteSnapshot: ModuleInfo;
preloadConfig: PreloadRemoteArgs;
origin: ModuleFederation;
}
errorLoadRemote
AsyncHook
如果加载 remotes 失败,则调用,从而启用自定义错误处理。可返回自定义的兜底逻辑。
async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>
type ErrorLoadRemoteOptions ={
id: string;
error: unknown;
options?: any;
from: 'build' | 'runtime';
lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
remote?: RemoteInfo;
expose?: string;
origin: ModuleFederation;
}
lifecycle 表示错误发生阶段:
beforeRequest: 处理 remote 请求参数阶段出错
afterResolve: 解析/拉取 manifest 阶段出错(常见于网络异常)
onLoad: 加载 exposes 模块阶段出错
beforeLoadShare: shared 初始化过程中加载 remoteEntry 出错
如果这个 hook 返回兜底模块,runtime 会使用这个值继续执行。如果诊断或日志插件返回 undefined,不会清空其他插件已经返回的兜底结果。
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 配置
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>
type BeforeLoadShareOptions ={
pkgName: string;
shareInfo?: Shared;
shared: Options['shared'];
origin: ModuleFederation;
}
afterLoadShare
SyncHook
在 loadShare 或 loadShareSync 成功解析 shared 依赖后触发。适合观察最终选中了哪个提供方和版本。
function afterLoadShare(args: AfterLoadShareOptions): void
type AfterLoadShareOptions ={
pkgName: string;
shareInfo?: Partial<Shared>;
selectedShared?: Partial<Shared>;
shared: Options['shared'];
shareScopeMap: ShareScopeMap;
lifecycle: 'loadShare' | 'loadShareSync';
origin: ModuleFederation;
}
errorLoadShare
SyncHook
在 shared 依赖解析失败,或者无法选中可用 shared 依赖时触发。适合诊断 shared 缺失、版本不匹配、eager 配置错误等问题。
function errorLoadShare(args: ErrorLoadShareOptions): void
type ErrorLoadShareOptions ={
pkgName: string;
shareInfo?: Partial<Shared>;
shared: Options['shared'];
shareScopeMap: ShareScopeMap;
lifecycle: 'loadShare' | 'loadShareSync';
origin: ModuleFederation;
error?: unknown;
recovered?: boolean;
}
initContainerShareScopeMap
SyncWaterfallHook
在 host(消费者)初始化 remote(生产者)共享池映射时触发,可用于对齐/重定向某个 scope 的共享池对象(例如把 scope1 直接指向 default,让两者共用同一套共享依赖池)。
function initContainerShareScopeMap(args: InitContainerShareScopeMapOptions): InitContainerShareScopeMapOptions
type InitContainerShareScopeMapOptions ={
shareScope: ShareScopeMap[string];
options: Options;
origin: ModuleFederation;
scopeName: string;
hostShareScopeMap?: ShareScopeMap;
}
resolveShare
SyncWaterfallHook
允许改写最终选中的共享模块结果。
resolveShare 触发时,运行时已经先选好了候选的 scope 和 version。这个阶段单独修改 args.scope、args.version 之类的字段,并不会自动影响最终结果。要真正改掉最终使用的 shared,需要改写 args.resolver,让它返回你希望使用的那条记录。
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;
}
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
在预加载处理程序执行任何预加载逻辑之前调用
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): Promise<void>
type BeforePreloadRemoteOptions ={
preloadOps: Array<PreloadRemoteArgs>;
options: Options;
origin: ModuleFederation;
}
generatePreloadAssets
AsyncHook
用于控制生成需要预加载的资源
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 用于拦截资源加载与工厂获取流程。
beforeInitRemote
AsyncHook
在调用 remoteEntry.init(...) 初始化 remote 容器之前触发。
async function beforeInitRemote(args: BeforeInitRemoteOptions): Promise<void>
type BeforeInitRemoteOptions ={
id?: string;
remoteInfo: RemoteInfo;
remoteSnapshot?: ModuleInfo;
origin: ModuleFederation;
}
afterInitRemote
AsyncHook
在 remote 容器初始化成功或失败后触发。如果 remote 已经初始化过,会带上 cached: true。
async function afterInitRemote(args: AfterInitRemoteOptions): Promise<void>
type AfterInitRemoteOptions ={
id?: string;
remoteInfo: RemoteInfo;
remoteSnapshot?: ModuleInfo;
remoteEntryExports?: RemoteEntryExports;
error?: unknown;
cached?: boolean;
origin: ModuleFederation;
}
beforeGetExpose
AsyncHook
在调用 remoteEntry.get(expose) 之前触发。
async function beforeGetExpose(args: BeforeGetExposeOptions): Promise<void>
type BeforeGetExposeOptions ={
id: string;
expose: string;
moduleInfo: RemoteInfo;
remoteEntryExports: RemoteEntryExports;
origin: ModuleFederation;
}
afterGetExpose
AsyncHook
在 remoteEntry.get(expose) 成功或失败后触发。
async function afterGetExpose(args: AfterGetExposeOptions): Promise<void>
type AfterGetExposeOptions ={
id: string;
expose: string;
moduleInfo: RemoteInfo;
remoteEntryExports: RemoteEntryExports;
moduleFactory?: () => unknown | Promise<unknown>;
error?: unknown;
origin: ModuleFederation;
}
beforeExecuteFactory
AsyncHook
在执行 exposed module factory 之前触发。loadRemote 使用 loadFactory: false 时不会触发。
async function beforeExecuteFactory(args: BeforeExecuteFactoryOptions): Promise<void>
type BeforeExecuteFactoryOptions ={
id: string;
expose: string;
moduleInfo: RemoteInfo;
loadFactory: boolean;
origin: ModuleFederation;
}
afterExecuteFactory
AsyncHook
在 exposed module factory 执行成功或失败后触发。loadRemote 使用 loadFactory: false 时不会触发。
async function afterExecuteFactory(args: AfterExecuteFactoryOptions): Promise<void>
type AfterExecuteFactoryOptions ={
id: string;
expose: string;
moduleInfo: RemoteInfo;
loadFactory: boolean;
exposeModule?: unknown;
error?: unknown;
origin: ModuleFederation;
}
createScript
SyncHook
用于修改加载资源时的 script
function createScript(args: CreateScriptOptions): CreateScriptHookReturn
type CreateScriptOptions ={
url: string;
attrs?: Record<string, any>;
remoteInfo?: RemoteInfo;
}
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
function fetch(manifestUrl: string, requestInit: RequestInit, remoteInfo?: RemoteInfo): 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'
});
},
}
};
createLink
SyncHook
用于修改预加载/样式加载时创建的 link 元素。
function createLink(args: CreateLinkOptions): HTMLLinkElement | void
type CreateLinkOptions ={
url: string;
attrs?: Record<string, any>;
remoteInfo?: RemoteInfo;
}
loadEntryError
AsyncHook
在 remoteEntry 加载失败(通常是脚本加载异常)时触发,可用于重试或自定义兜底。
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;
}
afterLoadEntry
AsyncHook
在 remoteEntry 加载成功、失败,或被 loadEntryError 恢复之后触发。
async function afterLoadEntry(args: AfterLoadEntryOptions): Promise<void>
type AfterLoadEntryOptions ={
origin: ModuleFederation;
remoteInfo: RemoteInfo;
remoteEntryExports?: RemoteEntryExports | false | void;
error?: unknown;
recovered?: boolean;
}
getModuleFactory
AsyncHook
在调用 remoteEntry.get(expose) 前触发,可自定义模块工厂获取逻辑。
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
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>;
};
// 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)。
function beforeBridgeRender(args: Record<string, any>): void | Record<string, any>
afterBridgeRender
SyncHook
在桥接渲染后触发。
function afterBridgeRender(args: Record<string, any>): void | Record<string, any>
beforeBridgeDestroy
SyncHook
在桥接销毁前触发。
function beforeBridgeDestroy(args: Record<string, any>): void | Record<string, any>
afterBridgeDestroy
SyncHook
在桥接销毁后触发。
function afterBridgeDestroy(args: Record<string, any>): void | Record<string, any>