@module-federation/runtime
- Can be combined with the build plug-in to share basic dependencies according to policies to reduce the number of module downloads and improve the loading speed of modules.
- Only consume part of the export of the remote module and will not fully download the remote module
- The runtime calling process can be extended through the module-runtime plug-in mechanism
API
import { init, loadRemote } from '@module-federation/runtime';
init({
name: '@demo/app-main',
remotes: [
{
name: "@demo/app2",
entry: "http://localhost:3006/remoteEntry.js",
alias: "app2"
},
],
});
loadRemote<{add: (...args: Array<number>)=> number }>("app2/util").then((md)=>{
md.add(1,2,3);
});
init
- Type:
init(options: InitOptions): void
- Create runtime instance , it can dynamically register remotes by re-call , but only one instance will exist.
- InitOptions:
type InitOptions {
name: string;
version?: string;
region?: `EnhancedRegion`;
remotes: Array<RemoteInfo>;
shared?: ShareInfos;
};
type RemoteInfo = (RemotesWithEntry | RemotesWithVersion) & {
alias?: string;
};
interface RemotesWithVersion {
name: string;
version: string;
}
interface RemotesWithEntry {
name: string;
entry: string;
}
type ShareInfos = {
[pkgName: string]: Share;
};
type Share = {
version: string;
useIn?: Array<string>;
from?: string;
lib: () => Module;
shareConfig?: SharedConfig;
deps?: Array<string>;
scope?: string | Array<string>;
};
loadRemote
-
Type: loadRemote(id: string)
-
Used to load initialized remote modules, when used with build plugins, it can be loaded directly through native import("remoteName/expose")
syntax, and the build plugin will automatically convert it into loadRemote
usage.
-
Example
import { init, loadRemote } from '@module-federation/runtime';
init({
name: '@demo/main-app',
remotes: [
{
name: '@demo/app2',
alias: 'app2',
entry: "http://localhost:3006/remoteEntry.js",
},
],
});
loadRemote('@demo/app2/util').then((m) => m.add(1, 2, 3));
loadRemote('app2/util').then((m) => m.add(1, 2, 3));
loadShare
-
Type: loadShare(pkgName: string)
-
Gets the share
dependency. When there are share
dependencies that match the current host
in the global environment, the existing and satisfying share
dependencies will be reused first. Otherwise, load its own dependencies and store them in the global cache.
-
This API
is generally not called directly by users, but is used by build plugins to convert their own dependencies.
-
Example
import { init, loadRemote, loadShare } from '@module-federation/runtime';
import React from 'react';
import ReactDOM from 'react-dom';
init({
name: '@demo/main-app',
remotes: [],
shared: {
react: {
version: '17.0.0',
scope: 'default',
lib: () => React,
shareConfig: {
singleton: true,
requiredVersion: '^17.0.0',
},
},
'react-dom': {
version: '17.0.0',
scope: 'default',
lib: () => ReactDOM,
shareConfig: {
singleton: true,
requiredVersion: '^17.0.0',
},
},
},
});
loadShare('react').then((reactFactory) => {
console.log(reactFactory());
});
preloadRemote
async function preloadRemote(preloadOptions: Array<PreloadRemoteArgs>) {}
type depsPreloadArg = Omit<PreloadRemoteArgs, 'depsRemote'>;
type PreloadRemoteArgs = {
nameOrAlias: string;
exposes?: Array<string>;
resourceCategory?: 'all' | 'sync';
depsRemote?: boolean | Array<depsPreloadArg>;
filter?: (assetUrl: string) => boolean;
};
Info: Only if the entry is manifest can more resources be loaded. The manifest plugin will be supported in 2024.Q1
Through preloadRemote
, module resources can be preloaded at an earlier stage to avoid waterfall requests. preloadRemote
can preload the following content:
- The
remoteEntry
of remote
expose
assets of remote
- Synchronous resources or asynchronous resources of
remote
remote
resources that remote
depends on
import { init, preloadRemote } from '@module-federation/runtime';
init({
name: '@demo/preload-remote',
remotes: [
{
name: '@demo/sub1',
entry: 'http://localhost:2001/vmok-manifest.json',
},
{
name: '@demo/sub2',
entry: 'http://localhost:2001/vmok-manifest.json',
},
{
name: '@demo/sub3',
entry: 'http://localhost:2001/vmok-manifest.json',
},
],
});
preloadRemote([
{
nameOrAlias: '@demo/sub1',
filter(assetUrl) {
return assetUrl.indexOf('ignore') === -1;
},
depsRemote: [{ nameOrAlias: '@demo/sub1-button' }],
},
]);
preloadRemote([
{
nameOrAlias: '@demo/sub2',
resourceCategory: 'all',
},
]);
preloadRemote([
{
nameOrAlias: '@demo/sub3',
resourceCategory: 'all',
exposes: ['add'],
},
]);
hooks
Lifecycle hooks for FederationHost interaction.
import { init } from '@module-federation/runtime'
import type { FederationRuntimePlugin } from '@module-federation/runtime';
const runtimePlugin: () => FederationRuntimePlugin =
function () {
return {
name: 'my-runtime-plugin',
beforeInit(args) {
console.log('beforeInit: ', args);
return args;
},
beforeRequest(args) {
console.log('beforeRequest: ', args);
return args;
},
afterResolve(args) {
console.log('afterResolve', args);
return args;
},
onLoad(args) {
console.log('onLoad: ', args);
return args;
},
async loadShare(args) {
console.log('loadShare:', args);
},
async beforeLoadShare(args) {
console.log('beforeloadShare:', args);
return args;
},
};
};
init({
name: '@demo/app-main',
remotes: [
{
name: "@demo/app2",
entry: "http://localhost:3006/remoteEntry.js",
alias: "app2"
},
],
plugins: [runtimePlugin()]
});
beforeInit
SyncWaterfallHook
Updates Federation Host configurations before the initialization process of remote containers.
function beforeInit(args: BeforeInitOptions): BeforeInitOptions
type BeforeInitOptions ={
userOptions: UserOptions;
options: FederationRuntimeOptions;
origin: FederationHost;
shareInfo: ShareInfos;
}
interface FederationRuntimeOptions {
id?: string;
name: string;
version?: string;
remotes: Array<Remote>;
shared: ShareInfos;
plugins: Array<FederationRuntimePlugin>;
inBrowser: boolean;
}
init
SyncHook
Called during the initialization of remote containers.
function init(args: InitOptions): void
type InitOptions ={
options: FederationRuntimeOptions;
origin: FederationHost;
}
beforeRequest
AsyncWaterfallHook
Invoked before resolving a remote container, useful for injecting the container or updating something ahead of the lookup.
async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>
type BeforeRequestOptions ={
id: string;
options: FederationRuntimeOptions;
origin: FederationHost;
}
afterResolve
AsyncWaterfallHook
Called after resolving a container, allowing redirection or modification of resolved information.
async function afterResolve(args: AfterResolveOptions): Promise<AfterResolveOptions>
type AfterResolveOptions ={
id: string;
pkgNameOrAlias: string;
expose: string;
remote: Remote;
options: FederationRuntimeOptions;
origin: FederationHost;
remoteInfo: RemoteInfo;
remoteSnapshot?: ModuleInfo;
}
onLoad
AsyncHook
Triggered once a federated module is loaded, allowing access and modification to the exports of the loaded file.
async function onLoad(args: OnLoadOptions): Promise<void>
type OnLoadOptions ={
id: string;
expose: string;
pkgNameOrAlias: string;
remote: Remote;
options: ModuleOptions;
origin: FederationHost;
exposeModule: any;
exposeModuleFactory: any;
moduleInstance: Module;
}
type ModuleOptions = {
remoteInfo: RemoteInfo;
host: FederationHost;
}
interface RemoteInfo {
name: string;
version?: string;
buildVersion?: string;
entry: string;
type: RemoteEntryType;
entryGlobalName: string;
shareScope: string;
}
handlePreloadModule
SyncHook
Handles preloading logic for federated modules.
function handlePreloadModule(args: HandlePreloadModuleOptions): void
type HandlePreloadModuleOptions ={
id: string;
name: string;
remoteSnapshot: ModuleInfo;
preloadConfig: PreloadRemoteArgs;
}
errorLoadRemote
AsyncHook
Invoked if loading a federated module fails, enabling custom error handling.
async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>
type ErrorLoadRemoteOptions ={
id: string;
error: unknown;
from: 'build' | 'runtime';
origin: FederationHost;
}
import { init, loadRemote } from '@module-federation/runtime'
import type { FederationRuntimePlugin } from '@module-federation/runtime';
const fallbackPlugin: () => FederationRuntimePlugin =
function () {
return {
name: 'fallback-plugin',
errorLoadRemote(args) {
const fallback = 'fallback'
return fallback;
},
};
};
init({
name: '@demo/app-main',
remotes: [
{
name: "@demo/app2",
entry: "http://localhost:3006/remoteEntry.js",
alias: "app2"
},
],
plugins: [fallbackPlugin()]
});
loadRemote('app2/un-existed-module').then(mod=>{
expect(mod).toEqual('fallback');
})
beforeLoadShare
AsyncWaterfallHook
Called before attempting to load or negotiate shared modules between federated apps.
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>
type BeforeLoadShareOptions ={
pkgName: string;
shareInfo?: Shared;
shared: Options['shared'];
origin: FederationHost;
}
resolveShare
SyncWaterfallHook
Allows manual resolution of shared module requests.
function resolveShare(args: ResolveShareOptions): ResolveShareOptions
type ResolveShareOptions ={
shareScopeMap: ShareScopeMap;
scope: string;
pkgName: string;
version: string;
GlobalFederation: Federation;
resolver: () => Shared | undefined;
}
import { init, loadRemote } from '@module-federation/runtime'
import type { FederationRuntimePlugin } from '@module-federation/runtime';
const customSharedPlugin: () => FederationRuntimePlugin =
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;
return shareScopeMap[scope][pkgName][version];
};
return args;
},
};
};
init({
name: '@demo/app-main',
shared: {
react: {
version: '17.0.0',
scope: 'default',
lib: () => React,
shareConfig: {
singleton: true,
requiredVersion: '^17.0.0',
},
},
},
plugins: [customSharedPlugin()]
});
window.React = ()=> 'Desired Shared';
loadShare("react").then((reactFactory)=>{
expect(reactFactory()).toEqual(window.React());
});
beforePreloadRemote
AsyncHook
Invoked before any preload logic is executed by the preload handler.
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): BeforePreloadRemoteOptions
type BeforePreloadRemoteOptions ={
preloadOps: Array<PreloadRemoteArgs>;
options: Options;
origin: FederationHost;
}
generatePreloadAssets
AsyncHook
Called for generating preload assets based on configurations.
async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise<PreloadAssets>
type GeneratePreloadAssetsOptions ={
origin: FederationHost;
preloadOptions: PreloadOptions[number];
remote: Remote;
remoteInfo: RemoteInfo;
remoteSnapshot: ModuleInfo;
globalSnapshot: GlobalModuleInfo;
}
interface PreloadAssets {
cssAssets: Array<string>;
jsAssetsWithoutEntry: Array<string>;
entryAssets: Array<EntryAssets>;
}
loaderHook
Plugin system for module loading operations.
createScript
SyncHook
function createScript(args: CreateScriptOptions): HTMLScriptElement | void
type CreateScriptOptions ={
url: string;
}
import { init } from '@module-federation/runtime'
import type { FederationRuntimePlugin } from '@module-federation/runtime';
const changeScriptAttributePlugin: () => FederationRuntimePlugin =
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;
}
}
};
};