The webpack module federation post resulted in many folks asking me if remote URLs need to be known at build-time? Or is there a way to specify the remotes after building, as runtime configuration somewhere.
Well, we have an easy way to load the component dynamically, here is a suggested method from the docs:
function loadComponent(scope, module) {
return async () => {
await __webpack_init_sharing__("default");
const container = window[scope];
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(module);
const Module = factory();
return Module;
};
}
loadComponent("Comic", "XKCD");
Let us dive into some details of each line in the above function:
__webpack_init_sharing__
is defined as:
{
__webpack_init_sharing__: {
expr: RuntimeGlobals.initializeSharing,
req: [RuntimeGlobals.initializeSharing],
type: "function",
assign: true
}
}
Where RuntimeGlobals.intiialzeSharing
is exported as:
exports.initializeSharing = "__webpack_require__.I";
__webpack_share_scopes
is defined as:
{
__webpack_share_scopes__: {
expr: RuntimeGlobals.shareScopeMap,
req: [RuntimeGlobals.shareScopeMap],
type: "object",
assign: false
},
}
shareScopeMap
would be:
exports.shareScopeMap = "__webpack_require__.S";
__webpack_share_scopes__.default
would resolve into:
{
"react-dom": {
"17.0.2": {
"from": "home-app",
"eager": true,
"loaded": 1
}
},
"react": {
"17.0.2": {
"from": "home-app",
"eager": true,
"loaded": 1
}
}
}
In case you are more curios of __webpack_require__
:
Object.keys(__webpack_require__);
Getting back to window[scope]
, in this context scope
is Comic
Object.getOwnPropertyNames(window[scope]);
init
accepts the shared scope object which is used as a shared scope in the remote container and is filled with the provided modules from a host. It can be leveraged to connect remote containers to a host container dynamically at runtime.
await window[scope].get(module)
would resolve into a function on invocation would result in the Module.
() => ((__webpack_require__( \"./app.jsx\")))
{__esModule: true, Symbol(Symbol.toStringTag): "Module"}
Module.default
would be the component that is shared.
The most important point to be mindful is that we need to load the container before attempting to dynamically connect a remote container.
Here is the same sample code re-written with dynamic remotes:
So, basically it boils down to:
Dynamically injecting the remote js
file to the head, so that it loads.
Loading the component for the given scope
and module
as highlighted above.
(Lazy) loading the component.
P.S: The patterns in the above sandbox is heavily influenced by dynamic-system-host sample, also the sandbox example uses System
custom component and is very verbose for better understanding, in the real world useage you need not be so verbose and might just have a single function that does all the three steps.
Feel free to share this article. You may as well ping me on Twitter.
Published