Steps to implement module federation
- Designate one app as the Host (ex: Container) and others as the Remotes (ex: Cart and Products)
- In the Remote, decide which modules (files) you want to make available to other projects (example: src/index.js)
- Set up Module Federation plugin to expose those files
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
mode: 'development',
devServer: {
port: 8082,
},
plugins: [
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./CartShow': './src/index',
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};- In the Host, decide which files you want to get from the remote
- Set up Module Federation plugin in Host to fetch those files
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
mode: 'development',
devServer: {
port: 8080,
},
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
products: 'products@http://localhost:8081/remoteEntry.js',
cart: 'cart@http://localhost:8082/remoteEntry.js',
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};- In the Host, refactor the entry point to load information asynchronously. This asynchronous behavior of dynamic imports with
import()is beneficial for loading modules on-demand, especially in scenarios where certain modules are not required immediately or are conditionally needed during runtime.
bootstrap.js // Load information
import 'products/ProductsIndex';
import 'cart/CartShow';
index.js
// import function call loads the imports asynchronously
// If you don't use import function, then you get an error cause webpack can't find it.
// You need to get the dependencies before running the bootstrap
import('./bootstrap');- In the Host, import whatever files you need from the remote. Example imports in index.js
import 'products/ProductsIndex';
import 'cart/CartShow';index.html has the id’s to show the information from other microfrontends.
<html>
<head></head>
<body>
<div id="dev-products"></div>
<div id="cart-dev"></div>
</body>
</html>products/src/index.js adds the info into dev-products
document.querySelector('#dev-products').innerHTML = cartText;Files Webpack generates from the Remote file (ex: index.js)
- Normal bundling process generates main.js and we still can run products as Standalone.
- Module Federation Plugin generates
- remoteEntry.js – contains list of files that are available from this project + directions on how to load them
- src_index.js – version of src/index.js that can be safely loaded into the browser
- faker.js – version of faker rhat can be safely loaded into the browser. (faker is a external library used in test)
Files Webpack generates from the Host
- Container has
- index.js from the Remote – import(‘./bootstrap.js’)
- bootstrap.js from the Remote – import ‘products/ProductIndex’
- Webpack generates
- main.js which contains content only from the index.js
- bootstrap.js – contains bootstrap.js. Webpack knows it has to fetch comething from products before running this file!
What happens when running Container
- main.js loaded and executed
- we need to load and execute bootstrap.js
- bootstrap needs a file from Products! Fetch remoteEntry.js to figure out how to fetch that
- Now we see that we need src_index.js, and that needs faker.js
- After getting them both, bootstrap.js can be now executed.
Module Federation Plugin parameters
Parameters in Host:
- name – Used only in Remote. For Host you can add it only for clarity
- remotes – list of projects that the Host can search to get additional code
- remotes.products – load the file at the listed UrlL if anyting in Host has an import like: import smth from ‘products’
- ‘products@http://localhost:8081/remoteEntry.js – where products is related to the name property in the Products webpack config file and after the @ sign is URL to the remoteEntry file
new ModuleFederationPlugin({
name: 'container',
remotes: {
products: 'products@http://localhost:8081/remoteEntry.js',
cart: 'cart@http://localhost:8082/remoteEntry.js',
},
}),Parameters in Remote:
- name – has to be same as the Host remotes url is using
- filename – sets the name of the manifest file. Leave it as ‘remoteEntry.js’ unless you’ve got a good reason to change it
- exposes has Aliases filenames. Alias should be a good name to understand what kind of data does it have.
new ModuleFederationPlugin({
name: 'products',
filename: 'remoteEntry.js',
exposes: {
'./ProductIndex': './src/index',
},
}),In Remotes Index.js files are used only in development process to make the component visible in the browser!