Module federation is implemented via the ModuleFederationPlugin
plugin provided natively by Webpack, which has two main concepts: Host (consuming other Remote) and Remote (being consumed by Host). Each project can be Host or Remote, or both.
As a Host, you need to configure the remote list and shared module.
As Remote, you need to configure the project name, packaging method, packaged filename, provided module, and Host shared module.
Webpack packaging principles
Module federation is an optimization based on WebPack, so we need to know how WebPack does packaging before we dive into module federation.
Reading the WebPack results, you can see that each webPack wraps all the resources in one immediately executed function, which avoids global contamination but also prevents external access to internal modules.
In the immediate execution function, Webpack uses the __webpack_modules__
object to save all module code, and then loads modules from __webpack_modules__
using the internally defined __webpack_require__
method. In addition, an array of webpackChunk
is exposed globally to communicate with multiple Webpack resources in both asynchronous loading and file splitting. This array is overridden by webpack's push method, It synchronously adds content to __webpack_modules__
when other resources add content to the 'webpackChunk' array to implement module integration.
Module federation is based on this mechanism, modifying the partial implementation of __webpack_require__
to remotely load resources at require and then merge them into __webpack_modules__
.
Tips:
webpackChunk
can be modified through configurationoutput.chunkLoadingGlobal
Webpack builds resources
JS modular mechanism has many standards, but in webpack all unified conversion to their own implementation of __webpack_require__
Webpack single file structure
In the webpack single-file structure, the source code for all modules is stored in the __webpack_modules__
object, which is then uniformly loaded using the internally defined __webpack_require__
method.
In this case, the external has no access to the internal running resources at all
(() => {
var __webpack_modules__ = {
"./src/other.js": () => {
// ...
},
};
var __webpack_module_cache__ = {}; // The cache of module export results
function __webpack_require__() {
// Internal module import method
// .....
}
// .....
// Entry file content
(() => {
__webpack_require__("./src/other.js");
})();
})();
webpack multi-file structure
In a multi-file structure, there are two places to store the module's source code, an internal __webpack_modules__
object and a global webpackChunk
array.
The webpackChunk
array acts as a bridge to load modules from other files into the __webpack_modules__
object of the entry file by overriding the push
method of the webpackChunk
array.
// index.js
(() => {
var __webpack_modules__ = {
"./src/other.js": () => {
// source code
},
};
var __webpack_module_cache__ = {}; // The cache of module export results
function __webpack_require__() {} // Internal module import method
// Expose an object globally
self["webpackChunk"].push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
(() => {
__webpack_require__("./src/other.js");
})();
})();
// ./src/other2.js
(self["webpackChunk"] = self["webpackChunk"] || []).push([
["src_other2_js"],
{
"./src/other2.js": () => {
console.log("in other2.js");
},
},
]);
ModuleFederationPlugin
new ModuleFederationPlugin({
name: "app1",
library: { type: "var", name: "app1" },
filename: "remoteEntry.js",
remotes: {
app2: "app2",
app3: "app3",
},
remoteType: "var",
exposes: {
antd: "./src/antd",
button: "./src/button",
},
shared: ["react", "react-dom"],
shareScope: "default",
});
The following describes the property configuration
name,required,Unique ID, used as the output module name (container), with name/{name}/name/{expose}.
library,optional,default value is
{ type: "var", name: options.name }
,Where name is the name of the umD, which is the name of the variable mounted globally.filename,optional,The file name after packaging.
remotes,optional,Indicates that the current application is a Host and can reference the Expose module in Remote.
remoteType,optional,default is
var
,("var"|"module"| "assign"|"this"|"window"|"self"|"global"|"commonjs"|"commonjs2"| "commonjs-module"|"amd"|"amd-require"|"umd"|"umd2"|"jsonp"|"system"|"promise"|"import"|"script"),The external type of the remote container.exposes,optional,Indicates that the current application is a Remote, exposes module that can be referenced by other hosts as import(name/{name}/name/{expose}).
shared,optional,It is mainly used to avoid multiple public dependencies in the project. If this attribute is configured, WebPack will first check whether the corresponding package exists in the local application when loading. If not, it will load the dependency package of the remote application.
shareScope,optional,Name of the shared scope used for all shared modules.
Tips
When using
module Federation
, you must remember to configure public dependencies into shared. In addition, two items must be configured with shared at the same time, otherwise an error will be reportedEntry file index JS itself should not have any logic. Put the logic in bootstrap JS, index JS to dynamically load bootstrap js。 If you put the logic directly into index In JS, an error will be reported. If it is a public dependency configuration shared, it can be solved with the eager parameter. The main reason is that if directly in the index JS execution logic will depend on JS exposed by remote. At this time, remote JS has not been loaded, there will be a problem.
Principle of ModuleFederationPlugin
Three main things have been done by ModuleFederationPlugin
.
- How to share dependencies: using
SharePlugin
- How to expose a module: using
ContainerPlugin
- How to reference a module: using
ContainerReferencePlugin
SharePlugin
This plugin makes common dependencies shareable
ContainerPlugin
The plug-in creates an entry for the specified public moduleentry. JS
after execution, an object will be hung on the window. The object has two methods, get 'and
init'The get
method is used to get the moduleThe init
method is used to initialize the container, which can provide shared modules.
// entry.js
var remote;
remote = (() => {
// ...
})();
// Get and init methods in remote object
var get = (module, getScope) => {
__webpack_require__.R = getScope;
getScope = __webpack_require__.o(moduleMap, module)
? moduleMap[module]()
: Promise.resolve().then(() => {
throw new Error('Module "' + module + '" does not exist in container.');
});
__webpack_require__.R = undefined;
return getScope;
};
var init = (shareScope, initScope) => {
if (!__webpack_require__.S) return;
var oldScope = __webpack_require__.S["default"];
var name = "default";
if (oldScope && oldScope !== shareScope)
throw new Error("Container initialization failed as it has already been initialized with a different share scope");
__webpack_require__.S[name] = shareScope;
return __webpack_require__.I(name, initScope);
};
// This exports getters to disallow modifications
__webpack_require__.d(exports, {
get: () => get,
init: () => init,
});
When using the remote module, write its own shared into the remote through init, and then get the exposed components in the remote. When it is a remote, judge whether there are available shared dependencies in the host. If so, load this part of the host's dependencies. If not, load its own dependencies.
ContainerReferencePlugin
The plug-in adds specific references to containers that are external resources and allows remote modules to be imported from these containers. When importing, the remote provided by the container user will be called for overloading.
Modules defined through remotes will also be displayed in
__webpack_modules__
But there will be no concrete implementation, which is similar to asynchronous import. New in webpack5__webpack_require__.e
method, execute the following three functions for the modules imported through the secondary method, and return only after all of them are successful.
__webpack_require__.f.consumes
is used to judge and consume the shared module. If this module already exists in the current environment, it will not request from the remote__webpack_require__.f.remotes
is used to connect containers__webpack_require__.f.j
used to load JS
Usage scenario
It is suitable for creating special component application services to manage all components and applications. The rest of the business layers only need to load the corresponding components and functional modules according to their own business needs
Unified module management, high code quality and fast construction speed. It is applicable to matrix app, visual page construction and other scenarios