When project A needs to share A component of Project B, both sides need to be consistent in subsequent iterations. There are two ways to do this:
- Copy the components of project B in full to project A
- This component is isolated and published to an internal NPM through which the component is loaded
Copy and paste is faster than standalone components, there is no need to separate components from project B, publish NPM, but not synchronize code in a timely manner. But when both projects use Webpack5, the federated module synchronizes the components of project B from project A with just A few lines of configuration.
What is module federation
Federated modules are a new feature available with WebPack 5 and are implemented through the ModuleFederationPlugin plugin that is provided natively with WebPack.
The federated module is mainly used to solve the problem of code sharing between multiple applications, which makes it more elegant to implement code sharing across applications
Code sharing through Module Federation is a two-way process, but there is a downgrade in each case. Module Federated can always load its own dependencies, but tries to use consumer dependencies before downloading. Less code redundancy, dependency sharing is like a single Webpack build.
Code sharing across applications
Dynamically import a remote module
Federated modules can be used through dynamic imports when remote projects cannot be written to a webpack configuration
The difference between dynamic and static imports is that you need to implement your own JS loading and container wiring
// host
function loadScript(src) {
return new Promise((res, rej) => {
const srcirpt = document.createElement("script");
script.src = src;
script.onload = res;
script.onfaild = rej;
document.body.appendChild(script);
});
}
// connect container
function loadComponent(scope, module) {
return async () => {
// Initialize the shared scope to fill it with supplied modules that know about this build and all remotes
await __webpack_init_sharing__("default");
const container = window[scope]; // Or initialize a container from somewhere else, which may provide shared modules
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(module);
const Module = factory();
return Module;
};
}
// Load the entry file for the remote module and connect
loadScript("http://localhost:3001/remote-entry.js").then(() => {
loadComponent("app", "button").then((module) => {
console.log(module);
});
});
// remote
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "app",
filename: "remote-entry.js",
exposes: {
button: "./src/button.js",
},
}),
],
};
Statically import a remote module
// host
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "host",
remotes: {
app: "app@http://localhost:3001/remote-entry.js",
},
shared: {
react: {
eager: true,
},
},
}),
],
};
// remote
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "app",
filename: "remote-entry.js",
exposes: {
button: "./src/button.js",
},
shared: ["react"],
}),
],
};
The three main functions of the federated module
remotes
remotes is used to declare which remote files will be referenced and from where to import remote resources
// In this configuration, can read from the remote http://localhost:3000/app-entry.js file to load the app module
new ModuleFederationPlugin({
name: "template",
remotes: {
app: "http://localhost:3000/app-entry.js",
},
});
exposes
exposes is used to declare which resources will be available for remote use
new ModuleFederationPlugin({
name: "template",
exposes: {
button: "./src/button.js",
},
});
shared
shared is used to declare what modules are shared between the main project and the remote project
To see more examples of module federation, visit the official repository.