Module Communication
Federated Module Communication¶
In a microfrontend architecture using Webpack 5's Module Federation, communication between federated modules requires careful design to ensure shared state, function calls, and dynamic loading are handled reliably. This section explores techniques for inter-microfrontend communication using shared variables (via shared modules) and dynamic imports (for runtime module loading).
Shared Variables via Shared Modules¶
Shared variables in federated modules are typically implemented through shared libraries defined in Webpack's shared configuration. These libraries act as a global context for state or utility functions, accessible by all feder, and are shared across builds.
Example: Shared State Service¶
// shared-state.js (shared module)
export const sharedState = { count: 0 };
export function increment() {
sharedState.count++;
}
// webpack.config.js (shared module config)
{
name: 'shared-module',
output: {
filename: 'shared-state.js',
path: __dirname,
library: {
type: 'var',
name: 'sharedState',
},
},
experiments: {
topological: true,
},
shared: {
'shared-state': {
singleton: true,
requiredVersion: '^1.0.0',
},
},
}
// federated-module.js (consumer)
import { sharedState } from 'shared-state';
console.log('Shared state:', sharedState.count); // Access shared variables
Key Considerations¶
- Synchronization: Shared variables should be immutable or managed by state libraries (e.g., Redux, Zustand) to avoid race conditions.
- Versioning: Use
sharedconfiguration in Webpack to enforce compatible versions of shared modules across builds. - Publishing & Consumption: Shared modules must be properly published to a shared registry or hosted endpoint, and referenced in both shared and consuming webpack configurations using the
sharedfield.
Dynamic Imports for Remote Module Access¶
Dynamic imports allow modules to load remote federated modules at runtime, enabling on-demand communication. This is critical for decoupling modules and avoiding static dependencies. Module Federation uses the remotes plugin to discover remote modules via remoteEntry.js endpoints.
Example: Loading a Remote Module¶
// federated-module.js
const remoteModule = await import('remote-module'); // Remote module name, not URL
// Call a function from the remote module
remoteModule.someFunction().then(result => {
console.log('Remote function result:', result);
});
Handling Asynchronous Loading¶
Use import() with async/await to ensure remote modules are fully loaded before invoking functions:
async function loadRemoteModule() {
const module = await import('remote-module'); // Remote module name
return module;
}
loadRemoteModule().then(module => {
module.init().catch(err => console.error('Remote module failed:', err));
});
remotes Plugin Configuration¶
Configure the remotes plugin in Webpack to map remote module names to remoteEntry.js endpoints:
// webpack.config.js (remotes plugin config)
{
name: 'remote-module',
output: {
filename: 'remote-entry.js',
path: __dirname,
library: {
type: 'var',
name: 'remoteEntry',
},
},
experiments: {
topological: true,
},
plugins: [
new webpack.container.RemotesPlugin({
remotes: {
'remote-module': 'https://remote-entry.js'
}
})
]
}
Best Practices¶
- Lazy Loading: Load remote modules only when needed to optimize performance.
- Error Handling: Implement fallbacks for failed dynamic imports (e.g., network errors).
- remotes Plugin: Configure the
remotesplugin in Webpack to map remote module names toremoteEntry.jsendpoints.
Diagram: Federated Communication Flow¶
+----------------+ +----------------+ +----------------+
| Microfrontend A |<---->| Shared Module |<---->| Microfrontend B |
| (Consumer) | | (Shared Lib) | | (Consumer) |
+----------------+ +----------------+ +----------------+
| | |
| | |
v v v
+----------------+ +----------------+ +----------------+
| Webpack 5 |<---->| Module Federation |<---->| Webpack 5 |
| (Shared Config)| | (Runtime) | | (Shared Config)|
+----------------+ +----------------+ +----------------+
Key takeaways¶
- Shared variables are implemented via Webpack's
sharedconfiguration and should be immutable or managed by state libraries. - Dynamic imports enable runtime loading of remote modules via the
remotesplugin, usingremoteEntry.jsendpoints for discovery. - Always handle asynchronous loading and errors to maintain robust communication between federated modules.
- Prioritize versioning and synchronization strategies to avoid conflicts in shared state.