I've successfully implemented the relatively new webpack 5 module federation system in my Angular 11 app, so it can load modules remotely on-demand from another build.
One thing I've found nothing about is how to handle assets like stylesheets and images. For example, there's a menu element in the federated module that requires its own styles:
I suppose that the styles could be compiled and put in the federated module's build assets, but that'd break links when it's used with and without federation.
I'm still experimenting with this, but I thought it'd be good to ask. Anybody had this issue?
Webpack 5 offers the Module Federation Plugin that lets you create multiple separate builds without dependencies between each other so they can be developed and deployed individually.
Asset Modules is a type of module that allows one to use asset files (fonts, icons, etc) without configuring additional loaders. Prior to webpack 5 it was common to use: raw-loader to import a file as a string.
Webpack supports the following module types natively: ECMAScript modules. CommonJS modules. AMD modules.
What is Webpack Module Federation? Module Federation is a new concept (architecture) of JavaScript application development, which then became a new feature in Webpack as a plugin. This approach is very much related to the idea of Micro Frontends:
It should be possible to expose and use any module type that webpack supports. Chunk loading should load everything needed in parallel (web: single round-trip to server). Overriding modules is a one-directional operation. Sibling containers cannot override each other's modules. Concept should be environment-independent.
Now webpack will automatically choose between resource and inline by following a default condition: a file with size less than 8kb will be treated as a inline module type and resource module type otherwise. You can change this condition by setting a Rule.parser.dataUrlCondition.maxSize option on the module rule level of your webpack configuration:
You can set the dependency as eager inside the advanced API of Module Federation, which doesn’t put the modules in an async chunk, but provides them synchronously. This allows us to use these shared modules in the initial chunk.
I think the most elegant way to achieve that would be to import your global styles.scss of the micro frontend into it's entry module component.scss and in your entry module component.ts set encapsulation: ViewEncapsulation.None
in order to break style encapsulation for it, which will in turn lead for that styles to be applied globally.
Well, I'm gonna post what I came up with, it's not pretty, but it seems to work fine for CSS assets.
First of all, I separated them in the remote module's build: angular.json:
"styles": [
"projects/xxx-admin/src/styles/styles.scss",
"projects/xxx-admin/src/styles/admin.scss",
{
"input": "projects/xxx-admin/src/styles/admin.scss",
"bundleName": "admin_module_styles",
"inject": false
}
],
This generates a CSS with a clear non-autogenerated name. Then we load the federated module from the app in the routes:
{
path: "admin",
component: AdminPanelComponent,
canActivate: [XxxAdminGuard],
loadChildren: () => {
const baseUrl = getAdminFrontendBaseUrl();
return loadAdminStyles().then(
() => loadRemoteModule({
remoteName: "xxx_admin",
remoteEntry: `${baseUrl}/remoteEntry.js`,
exposedModule: "AdminModule",
}).then((m) => m.AdminModule));
},
},
...
export function loadAdminStyles(): Promise<void> {
return new Promise((resolve => {
const baseUrl = getAdminFrontendBaseUrl();
const el = document.getElementById("admin-module-styles");
// Load one instance, do it like this to handle errors and retrying
if (el) {
el.remove();
}
const headEl = document.getElementsByTagName("head")[0];
const styleLinkEl = document.createElement("link");
styleLinkEl.rel = "stylesheet";
styleLinkEl.id = "admin-module-styles";
styleLinkEl.href = `${baseUrl}/admin_module_styles.css`;
headEl.appendChild(styleLinkEl);
resolve();
}));
}
It's suboptimal, but I couldn't figure anything better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With